#!/usr/bin/env slsh % A fast /proc filesystem checker for Linux. It looks for hidden processes. % Copyright (C) 2008 John E. Davis % % The chkproc2.sl script is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License as % published by the Free Software Foundation; either version 2 of the % License, or (at your option) any later version. % % This script is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU % General Public License for more details. It is available online at % . % require ("cmdopt"); require ("glob"); private variable FIRST_PID = 1; private variable LAST_PID = 99999; private variable FIRST_GID = 1; % does not include 0 private variable LAST_GID = 0xFFFF; private variable Script_Version_String = "0.1.0"; define get_info (dir) { variable exe = readlink (path_concat (dir, "exe")); if (exe == NULL) { % Could be an internal pgm, or process has no permission to % read the link. variable fp = fopen (path_concat (dir, "status"), "r"); if ((fp == NULL) || (-1 == fgets (&exe, fp))) return NULL; variable len = strlen (exe); if (strncmp (exe, "Name:\t", 6)) exe = "Unknown"; else exe = strcat ("<", substr (exe, 7, len-7), ">"); } return struct { exe = exe, }; } define print_info (pid, dir) { variable s = get_info (dir); if (s == NULL) return; () = fprintf (stdout, "%d:%s\n", pid, s.exe); } private define exit_version () { () = fprintf (stdout, "Version: %S\n", Script_Version_String); exit (0); } private define exit_usage () { variable fp = stderr; () = fprintf (fp, "Usage: %s [options]\n", __argv[0]); variable opts = [ "Options:\n", " -g, --gids=RANGE Use specified GID range\n", " -q, --quiet Run quietly, showing only warnings\n", " -v, --version Print version\n", " -h, --help This message\n", ]; foreach (opts) { variable opt = (); () = fputs (opt, fp); } exit (1); } define slsh_main () { variable grange = "${FIRST_GID}:${LAST_GID}"$; variable quiet = 0; variable c = cmdopt_new (); c.add ("q|quiet", &quiet; default=1); c.add ("g|gid", &grange; type="str"); c.add("h|help", &exit_usage); c.add("v|version", &exit_version); variable i = c.process (__argv, 1); if (i != __argc) exit_usage (1); variable gmin, gmax, gdir; if (grange != NULL) { grange = strtrans (grange, "-:", " "); if (2 != sscanf (grange, "%d %d", &gmin, &gmax)) { () = fprintf (stderr, "Error parsing group range\n"); exit (1); } gdir = 1; if (gmin > gmax) gdir = -1; } variable pid, gid; variable suspect_pid_list = {}; variable hidden_pid_list = {}; variable dir; variable is_thread = Char_Type[LAST_PID+1]; () = setgid (0); variable dirs = glob ("/proc/*"); _for pid (FIRST_PID, LAST_PID, 1) { dir = sprintf ("/proc/%d", pid); if (0 == chdir (dir)) { foreach (listdir (strcat (dir, "/task"))) { variable tpid = (); tpid = atoi (tpid); if (tpid != pid) is_thread[tpid] = 1; } ifnot (quiet) print_info (pid, dir); if (NULL == wherefirst (dirs == dir)) { % check for race condition dirs = glob("/proc/*"); if ((NULL == wherefirst (dirs == dir)) && (0 == chdir (dir))) list_append (hidden_pid_list, pid); } continue; } if (-1 != kill (pid, 0)) { % pid present -- could be the result of a race condition if (0 == chdir (dir)) { ifnot (quiet) print_info (pid, dir); continue; } if (-1 != kill (pid, 0)) { () = fprintf (stderr, "WARNING: pid %d exists, but chdir %s fails\n", pid, dir); list_append (suspect_pid_list, pid); } } } foreach pid (hidden_pid_list) { if (is_thread[pid]) { ifnot (quiet) vmessage ("pid %d appears to be a thread", pid); continue; } dir = sprintf ("/proc/%d", pid); if (0 == chdir (dir)) { () = fprintf (stderr, "WARNING: %s is hidden and does not appear to be a thread\n", dir); print_info (pid, dir); } } ifnot (length (suspect_pid_list)) exit (0); ifnot (quiet) vmessage ("Checking suspect pids using gid range g=%d:%d", gmin, gmax); foreach pid (suspect_pid_list) { ifnot (quiet) vmessage ("Processing suspect pid %d", pid); if (-1 == kill (pid, 0)) { () = fprintf (stderr, "Suspect pid %d appears to have exited\n", pid); continue; } dir = sprintf ("/proc/%d", pid); _for gid (gmin, gmax, gdir) { if (0 != setgid (gid)) continue; if (0 == chdir (dir)) { () = fprintf (stdout, "WARNING: %s needs gid=%d for access\n", dir, gid); print_info (pid, dir); break; } } then { () = fprintf (stderr, "Could not access %s using gids in the range %d-%d\n", dir, gmin, gmax); } } exit (0); }