diff options
author | Craig Small <csmall@users.sourceforge.net> | 2012-07-28 19:00:56 +1000 |
---|---|---|
committer | Craig Small <csmall@users.sourceforge.net> | 2012-07-28 19:00:56 +1000 |
commit | 2d7933d23257becad992f86c95fc22c7862e7d35 (patch) | |
tree | b385f04ca0ef5ce5336b0ad26df3474f18382653 /src | |
parent | 09362d5554e1c6e523dd02d1a6863242dd0ba6e6 (diff) | |
parent | 7afa9fa7eb16cc5f53e38bda80e475985a372b6b (diff) |
Merge branch 'master' of ssh://psmisc.git.sourceforge.net/gitroot/psmisc/psmisc
Conflicts:
doc/killall.1
doc/pstree.1
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/fuser.c | 19 | ||||
-rw-r--r-- | src/fuser.h | 2 | ||||
-rw-r--r-- | src/killall.c | 85 | ||||
-rw-r--r-- | src/lists.h | 2 | ||||
-rw-r--r-- | src/pstree.c | 124 | ||||
-rw-r--r-- | src/timeout.c | 267 | ||||
-rw-r--r-- | src/timeout.h | 36 |
8 files changed, 415 insertions, 125 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 1d400b5..9d6f777 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,9 @@ if WANT_PEEKFD_MIPS endif fuser_SOURCES = fuser.c comm.h signals.c signals.h i18n.h fuser.h lists.h +if WANT_TIMEOUT_STAT + fuser_SOURCES += timeout.c timeout.h +endif fuser_LDADD = @LIBINTL@ @@ -54,7 +57,7 @@ signames.h: signames.c Makefile export LC_ALL=C ; \ @CPP@ -dM $< |\ tr -s '\t ' ' ' | sort -n -k 3 | sed \ - 's:#define SIG\([A-Z]\+[0-9]*\) \([0-9]\+\) *\(\|/\*.*\)$$:{\ \2,"\1" },:p;d' | \ + 's:#define SIG\([A-Z][A-Z]*[0-9]*\) \([0-9][0-9]*\).*$\:{\ \2,"\1" },:p;d' | \ grep -v '[0-9][0-9][0-9]' >signames.h || \ { rm -f signames.h; exit 1; } grep '^{ 1,"HUP" },$$' signames.h >/dev/null || \ diff --git a/src/fuser.c b/src/fuser.c index 2d70b46..e29a345 100644 --- a/src/fuser.c +++ b/src/fuser.c @@ -111,9 +111,13 @@ static dev_t device(const char *path); #endif static char *expandpath(const char *path); -typedef int (*stat_t)(const char*, struct stat*); #ifdef WITH_TIMEOUT_STAT +# if (WITH_TIMEOUT_STAT == 2) +# include "timeout.h" +# else +typedef int (*stat_t)(const char*, struct stat*); static int timeout(stat_t func, const char *path, struct stat *buf, unsigned int seconds); +# endif #else #define timeout(func,path,buf,dummy) (func)((path),(buf)) #endif /* WITH_TIMEOUT_STAT */ @@ -125,7 +129,7 @@ static void usage(const char *errormsg) fprintf(stderr, _ - ("Usage: fuser [-fMuv] [-a|-s] [-4|-6] [-c|-m|-n SPACE] [-k [-i] [-SIGNAL]] NAME...\n" + ("Usage: fuser [-fMuvw] [-a|-s] [-4|-6] [-c|-m|-n SPACE] [-k [-i] [-SIGNAL]] NAME...\n" " fuser -l\n" " fuser -V\n" "Show which processes use the named files, sockets, or filesystems.\n\n" " -a,--all display unused files too\n" @@ -261,7 +265,7 @@ scan_procs(struct names *names_head, struct inode_list *ino_head, if (root_stat) free(root_stat); if (cwd_stat) free(cwd_stat); if (exe_stat) free(exe_stat); -#ifndef __linux__ +#if !defined (__linux__) && !defined (__CYGWIN__) check_dir(pid, "lib", dev_head, ino_head, uid, ACCESS_MMAP, sockets, netdev); check_dir(pid, "mmap", dev_head, ino_head, uid, ACCESS_MMAP, @@ -928,7 +932,9 @@ int main(int argc, char *argv[]) #endif netdev = find_net_dev(); +#ifndef __CYGWIN__ /* Cygwin doesn't support /proc/net/unix */ fill_unix_cache(&unixsockets); +#endif for (argc_cnt = 1; argc_cnt < argc; argc_cnt++) { current_argv = argv[argc_cnt]; @@ -1371,7 +1377,7 @@ check_dir(const pid_t pid, const char *dirname, struct device_list *dev_head, st.st_ino = 0; if ((thedev = device(filepath)) < 0) #else - if (!st.st_ino && timeout(stat, filepath, &st, 5) != 0) + if (timeout(stat, filepath, &st, 5) != 0) #endif { if (errno != ENOENT) { @@ -1379,6 +1385,9 @@ check_dir(const pid_t pid, const char *dirname, struct device_list *dev_head, filepath, strerror(errno)); } } else { +#ifndef _LISTS_H + thedev = st.st_dev; +#endif if (thedev == netdev) { for (sock_tmp = sockets; sock_tmp != NULL; sock_tmp = sock_tmp->next) { @@ -1783,7 +1792,7 @@ scan_swaps(struct names *names_head, struct inode_list *ino_head, * Execute stat(2) system call with timeout to avoid deadlock * on network based file systems. */ -#ifdef WITH_TIMEOUT_STAT +#if defined(WITH_TIMEOUT_STAT) && (WITH_TIMEOUT_STAT == 1) static sigjmp_buf jenv; diff --git a/src/fuser.h b/src/fuser.h index 21eb720..242ce19 100644 --- a/src/fuser.h +++ b/src/fuser.h @@ -86,7 +86,7 @@ struct mount_list { struct mount_list *next; }; -#if defined (__GNUC__) && defined(__OPTIMIZE__) +#if defined (__GNUC__) && defined(__OPTIMIZE__) && !defined (__CYGWIN__) # include "lists.h" typedef struct mntinfo_s { list_t this; diff --git a/src/killall.c b/src/killall.c index 8145535..a26c458 100644 --- a/src/killall.c +++ b/src/killall.c @@ -2,7 +2,7 @@ * killall.c - kill processes by name or list PIDs * * Copyright (C) 1993-2002 Werner Almesberger - * Copyright (C) 2002-2007 Craig Small + * Copyright (C) 2002-2012 Craig Small * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,11 +80,9 @@ #define ER_UNKWN -3 #define ER_OOFRA -4 -#define NOT_PIDOF_OPTION if (pidof) usage(NULL) - static int verbose = 0, exact = 0, interactive = 0, reg = 0, quiet = 0, wait_until_dead = 0, process_group = 0, - ignore_case = 0, pidof; + ignore_case = 0; static long younger_than = 0, older_than = 0; static int @@ -130,7 +128,7 @@ uptime() char buf[2048]; FILE* file; if (!(file=fopen( PROC_BASE "/uptime", "r"))) { - fprintf(stderr, "error opening uptime file\n"); + fprintf(stderr, "killall: error opening uptime file\n"); exit(1); } savelocale = setlocale(LC_NUMERIC, NULL); @@ -206,7 +204,7 @@ match_process_uid(pid_t pid, uid_t uid) fclose(f); if (re==-1) { - fprintf(stderr, _("Cannot get UID from process status\n")); + fprintf(stderr, _("killall: Cannot get UID from process status\n")); exit(1); } return re; @@ -232,7 +230,7 @@ build_regexp_list(int names, char **namelist) { if (regcomp(®list[i], namelist[i], flag) != 0) { - fprintf(stderr, _("Bad regular expression: %s\n"), namelist[i]); + fprintf(stderr, _("killall: Bad regular expression: %s\n"), namelist[i]); exit (1); } } @@ -383,7 +381,7 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) continue; } process_age_sec = process_age(proc_stt_jf); - assert(process_age_sec > 0); + assert(process_age_sec >= 0L); } (void) fclose (file); @@ -443,8 +441,8 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) if (exact && !okay) { if (verbose) - fprintf (stderr, _("skipping partial match %s(%d)\n"), comm, - pid_table[i]); + fprintf (stderr, _("killall: skipping partial match %s(%d)\n"), + comm, pid_table[i]); continue; } got_long = okay; @@ -544,8 +542,8 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) pgids[i] = id; if (id < 0) { - fprintf (stderr, "getpgid(%d): %s\n", pid_table[i], - strerror (errno)); + fprintf (stderr, "killall: getpgid(%d): %s\n", + pid_table[i], strerror (errno)); } for (j = 0; j < i; j++) if (pgids[j] == id) @@ -555,14 +553,7 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) } if (interactive && !ask (comm, id, signal)) continue; - if (pidof) - { - if (found) - putchar (' '); - printf ("%d", id); - found |= 1 << (found_name >= 0 ? found_name : 0); - } - else if (kill (process_group ? -id : id, signal) >= 0) + if (kill (process_group ? -id : id, signal) >= 0) { if (verbose) fprintf (stderr, _("Killed %s(%s%d) with signal %d\n"), got_long ? command : @@ -576,12 +567,10 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) fprintf (stderr, "%s(%d): %s\n", got_long ? command : comm, id, strerror (errno)); } - if (!quiet && !pidof) + if (!quiet) for (i = 0; i < names; i++) if (!(found & (1 << i))) fprintf (stderr, _("%s: no process found\n"), namelist[i]); - if (pidof) - putchar ('\n'); if (names) /* killall returns a zero return code if at least one process has * been killed for each listed command. */ @@ -614,20 +603,7 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) static void -usage_pidof (void) -{ - fprintf (stderr, _( - "Usage: pidof [ -eg ] NAME...\n" - " pidof -V\n\n" - " -e require exact match for very long names;\n" - " skip if the command line is unavailable\n" - " -g show process group ID instead of process ID\n" - " -V display version information\n\n")); -} - - -static void -usage_killall (const char *msg) +usage (const char *msg) { if (msg != NULL) fprintf(stderr, "%s\n", msg); @@ -661,24 +637,15 @@ usage_killall (const char *msg) " (must precede other arguments)\n")); #endif /*WITH_SELINUX*/ fputc('\n', stderr); + exit(1); } -static void -usage (const char *msg) -{ - if (pidof) - usage_pidof (); - else - usage_killall (msg); - exit (1); -} - void print_version() { - fprintf(stderr, "%s (PSmisc) %s\n", pidof ? "pidof" : "killall", VERSION); + fprintf(stderr, "killall (PSmisc) %s\n", VERSION); fprintf(stderr, _( - "Copyright (C) 1993-2005 Werner Almesberger and Craig Small\n\n")); + "Copyright (C) 1993-2012 Werner Almesberger and Craig Small\n\n")); fprintf(stderr, _( "PSmisc comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it under\n" @@ -748,7 +715,6 @@ main (int argc, char **argv) name++; else name = *argv; - pidof = strcmp (name, "killall"); sig_num = SIGTERM; @@ -766,52 +732,43 @@ main (int argc, char **argv) process_group = 1; break; case 'y': - NOT_PIDOF_OPTION; strncpy(yt, optarg, 16); yt[15] = '\0'; if ( 0 >= (younger_than = parse_time_units(yt) ) ) usage(_("Invalid time format")); break; case 'o': - NOT_PIDOF_OPTION; strncpy(ot, optarg, 16); ot[15] = '\0'; if ( 0 >= (older_than = parse_time_units(ot) ) ) usage(_("Invalid time format")); break; case 'i': - NOT_PIDOF_OPTION; interactive = 1; break; case 'l': - NOT_PIDOF_OPTION; list_signals(); return 0; break; case 'q': - NOT_PIDOF_OPTION; quiet = 1; break; case 'r': - NOT_PIDOF_OPTION; reg = 1; break; case 's': sig_num = get_signal (optarg, "killall"); break; case 'u': - NOT_PIDOF_OPTION; if (!(pwent = getpwnam(optarg))) { fprintf (stderr, _("Cannot find user %s\n"), optarg); exit (1); } break; case 'v': - NOT_PIDOF_OPTION; verbose = 1; break; case 'w': - NOT_PIDOF_OPTION; wait_until_dead = 1; break; case 'I': @@ -819,7 +776,6 @@ main (int argc, char **argv) if (strcmp(argv[optind-1],"-I") == 0 || strncmp(argv[optind-1],"--",2) == 0) { ignore_case = 1; } else { - NOT_PIDOF_OPTION; sig_num = get_signal (argv[optind]+1, "killall"); } break; @@ -829,7 +785,6 @@ main (int argc, char **argv) print_version(); return 0; } - NOT_PIDOF_OPTION; sig_num = get_signal (argv[optind]+1, "killall"); break; #ifdef WITH_SELINUX @@ -868,12 +823,14 @@ main (int argc, char **argv) #endif usage(NULL); - if (argc - myoptind > MAX_NAMES + 1) { - fprintf (stderr, _("Maximum number of names is %d\n"), MAX_NAMES); + if (argc - myoptind > MAX_NAMES) { + fprintf (stderr, _("killall: Maximum number of names is %d\n"), + MAX_NAMES); exit (1); } if (!have_proc_self_stat()) { - fprintf (stderr, _("%s lacks process entries (not mounted ?)\n"), PROC_BASE); + fprintf (stderr, _("killall: %s lacks process entries (not mounted ?)\n"), + PROC_BASE); exit (1); } argv = argv + myoptind; diff --git a/src/lists.h b/src/lists.h index 4ca8cb7..ae8929e 100644 --- a/src/lists.h +++ b/src/lists.h @@ -68,7 +68,7 @@ extern inline void attribute((used,__gnu_inline__,always_inline,__artificial__)) asm volatile ("lfetch [%0]" :: "r" (x)) #elif defined(__powerpc64__) asm volatile ("dcbt 0,%0" :: "r" (x)) -#elif !defined(__CYGWIN__) && defined(__i386__) +#elif !defined(__CYGWIN__) && !defined(__PIC__) && defined(__i386__) asm volatile ("661:\n\t" ".byte 0x8d,0x74,0x26,0x00\n" "\n662:\n" diff --git a/src/pstree.c b/src/pstree.c index 80cfcec..9d9e8d5 100644 --- a/src/pstree.c +++ b/src/pstree.c @@ -2,7 +2,7 @@ * pstree.c - display process tree * * Copyright (C) 1993-2002 Werner Almesberger - * Copyright (C) 2002-2009 Craig Small + * Copyright (C) 2002-2012 Craig Small * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,6 +73,7 @@ typedef struct _proc { char **argv; /* only used : argv[0] is 1st arg; undef if argc < 1 */ int argc; /* with -a : number of arguments, -1 if swapped */ pid_t pid; + pid_t pgid; uid_t uid; #ifdef WITH_SELINUX security_context_t scontext; @@ -122,7 +123,7 @@ static int capacity = 0; static int *width = NULL; static int *more = NULL; -static int print_args = 0, compact = 1, user_change = 0, pids = 0, +static int print_args = 0, compact = 1, user_change = 0, pids = 0, pgids = 0, show_parents = 0, by_pid = 0, trunc = 1, wait_end = 0; #ifdef WITH_SELINUX static int show_scontext = 0; @@ -265,7 +266,7 @@ static PROC *new_proc(const char *comm, pid_t pid, uid_t uid) exit(1); } strncpy(new->comm, comm, COMM_LEN+2); - new->comm[COMM_LEN+1]='\0'; /* make sure nul terminated*/ + new->comm[COMM_LEN+1] = '\0'; /* make sure nul terminated*/ new->pid = pid; new->uid = uid; new->flags = 0; @@ -295,8 +296,8 @@ static void add_child(PROC * parent, PROC * child) if (by_pid) { if ((*walk)->child->pid > child->pid) break; - } else if ((cmp = strcmp((*walk)->child->comm, child->comm)) > 0) - break; + } else if ((cmp = strcmp((*walk)->child->comm, child->comm)) > 0) { + break; } else if (!cmp && (*walk)->child->uid > child->uid) break; new->next = *walk; @@ -334,13 +335,35 @@ static void set_args(PROC * this, const char *args, int size) this->argv[i] = start = strchr(start, 0) + 1; } +static void +rename_proc(PROC *this, const char *comm, uid_t uid) +{ + PROC *tmp_child, *parent; + CHILD **walk; + + strncpy(this->comm, comm, COMM_LEN+2); + this->comm[COMM_LEN+1] = '\0'; + this->uid = uid; + + /* Re-sort children in parent, now we have a name */ + if (!by_pid && this->parent) { + parent = this->parent; + for (walk = &parent->children; *walk; walk = &(*walk)->next) { + if ( ((*walk)->next != NULL) && strcmp((*walk)->child->comm, (*walk)->next->child->comm) > 0 ) { + tmp_child = (*walk)->child; + (*walk)->child = (*walk)->next->child; + (*walk)->next->child = tmp_child; + } + } + } +} #ifdef WITH_SELINUX static void -add_proc(const char *comm, pid_t pid, pid_t ppid, uid_t uid, +add_proc(const char *comm, pid_t pid, pid_t ppid, pid_t pgid, uid_t uid, const char *args, int size, char isthread, security_context_t scontext) #else /*WITH_SELINUX */ static void -add_proc(const char *comm, pid_t pid, pid_t ppid, uid_t uid, +add_proc(const char *comm, pid_t pid, pid_t ppid, pid_t pgid, uid_t uid, const char *args, int size, char isthread) #endif /*WITH_SELINUX */ { @@ -353,22 +376,36 @@ add_proc(const char *comm, pid_t pid, pid_t ppid, uid_t uid, this = new_proc(comm, pid, uid); #endif /*WITH_SELINUX */ else { - strncpy(this->comm, comm, COMM_LEN+2); - this->comm[COMM_LEN+1]; - this->uid = uid; + rename_proc(this, comm, uid); } if (args) set_args(this, args, size); if (pid == ppid) ppid = 0; + this->pgid = pgid; if (isthread) this->flags |= PFLAG_THREAD; - if (!(parent = find_proc(ppid))) + if (!(parent = find_proc(ppid))) { #ifdef WITH_SELINUX parent = new_proc("?", ppid, 0, scontext); #else /*WITH_SELINUX */ parent = new_proc("?", ppid, 0); #endif /*WITH_SELINUX */ + /* When using kernel 3.3 with hidepid feature enabled on /proc + * then we need fake root pid */ + if (!isthread && pid != 1) { + PROC *root; + if (!(root = find_proc(1))) { +#ifdef WITH_SELINUX + root = new_proc("?", 1, 0, scontext); +#else /*WITH_SELINUX */ + root = new_proc("?", 1, 0); +#endif + } + add_child(root, parent); + parent->parent = root; + } + } add_child(parent, this); this->parent = parent; } @@ -451,6 +488,10 @@ dump_tree(PROC * current, int level, int rep, int leaf, int last, out_char(info++ ? ',' : '('); (void) out_int(current->pid); } + if (pgids) { + out_char(info++ ? ',' : '('); + (void) out_int(current->pgid); + } if (user_change && prev_uid != current->uid) { out_char(info++ ? ',' : '('); if ((pw = getpwuid(current->uid))) @@ -614,7 +655,7 @@ static void read_proc(void) size_t buffer_size; char readbuf[BUFSIZ + 1]; char *tmpptr; - pid_t pid, ppid; + pid_t pid, ppid, pgid; int fd, size; int empty; #ifdef WITH_SELINUX @@ -669,7 +710,7 @@ static void read_proc(void) /* We now have readbuf with pid and cmd, and tmpptr+2 * with the rest */ /*printf("tmpptr: %s\n", tmpptr+2); */ - if (sscanf(tmpptr + 2, "%*c %d", &ppid) == 1) { + if (sscanf(tmpptr + 2, "%*c %d %d", &ppid, &pgid) == 2) { DIR *taskdir; struct dirent *dt; char *taskpath; @@ -691,17 +732,17 @@ static void read_proc(void) if (thread != pid) { #ifdef WITH_SELINUX if (print_args) - add_proc(threadname, thread, pid, st.st_uid, + add_proc(threadname, thread, pid, pgid, st.st_uid, threadname, strlen (threadname) + 1, 1,scontext); else - add_proc(threadname, thread, pid, st.st_uid, + add_proc(threadname, thread, pid, pgid, st.st_uid, NULL, 0, 1, scontext); #else /*WITH_SELINUX */ if (print_args) - add_proc(threadname, thread, pid, st.st_uid, + add_proc(threadname, thread, pid, pgid, st.st_uid, threadname, strlen (threadname) + 1, 1); else - add_proc(threadname, thread, pid, st.st_uid, + add_proc(threadname, thread, pid, pgid, st.st_uid, NULL, 0, 1); #endif /*WITH_SELINUX */ } @@ -713,9 +754,9 @@ static void read_proc(void) free(taskpath); if (!print_args) #ifdef WITH_SELINUX - add_proc(comm, pid, ppid, st.st_uid, NULL, 0, 0, scontext); + add_proc(comm, pid, ppid, pgid, st.st_uid, NULL, 0, 0, scontext); #else /*WITH_SELINUX */ - add_proc(comm, pid, ppid, st.st_uid, NULL, 0, 0); + add_proc(comm, pid, ppid, pgid, st.st_uid, NULL, 0, 0); #endif /*WITH_SELINUX */ else { sprintf(path, "%s/%d/cmdline", PROC_BASE, pid); @@ -734,10 +775,10 @@ static void read_proc(void) if (size) buffer[size++] = 0; #ifdef WITH_SELINUX - add_proc(comm, pid, ppid, st.st_uid, + add_proc(comm, pid, ppid, pgid, st.st_uid, buffer, size, 0, scontext); #else /*WITH_SELINUX */ - add_proc(comm, pid, ppid, st.st_uid, + add_proc(comm, pid, ppid, pgid, st.st_uid, buffer, size, 0); #endif /*WITH_SELINUX */ } @@ -757,40 +798,11 @@ static void read_proc(void) } } - -#if 0 - -/* Could use output of ps achlx | awk '{ print $3,$4,$2,$13 }' */ - -static void read_stdin(void) -{ - char comm[PATH_MAX + 1]; - char *cmd; - int pid, ppid, uid; - - while (scanf("%d %d %d %s\n", &pid, &ppid, &uid, comm) == 4) { - if (cmd = strrchr(comm, '/')) - cmd++; - else - cmd = comm; - if (*cmd == '-') - cmd++; -#ifdef WITH_SELINUX - add_proc(cmd, pid, ppid, uid, NULL, 0, NULL); -#else /*WITH_SELINUX */ - add_proc(cmd, pid, ppid, uid, NULL, 0); -#endif /*WITH_SELINUX */ - } -} - -#endif - - static void usage(void) { fprintf(stderr, _ - ("Usage: pstree [ -a ] [ -c ] [ -h | -H PID ] [ -l ] [ -n ] [ -p ] [ -u ]\n" + ("Usage: pstree [ -a ] [ -c ] [ -h | -H PID ] [ -l ] [ -n ] [ -p ] [ -g ] [ -u ]\n" " [ -A | -G | -U ] [ PID | USER ]\n" " pstree -V\n" "Display a tree of processes.\n\n" " -a, --arguments show command line arguments\n" @@ -799,6 +811,7 @@ static void usage(void) " -h, --highlight-all highlight current process and its ancestors\n" " -H PID,\n" " --highlight-pid=PID highlight this process and its ancestors\n" + " -g, --show-pgids show process group ids; implies -c\n" " -G, --vt100 use VT100 line drawing characters\n" " -l, --long don't truncate long lines\n" " -n, --numeric-sort sort output by PID\n" @@ -850,6 +863,7 @@ int main(int argc, char **argv) {"long", 0, NULL, 'l'}, {"numeric-sort", 0, NULL, 'n'}, {"show-pids", 0, NULL, 'p'}, + {"show-pgids", 0, NULL, 'g'}, {"show-parents", 0, NULL, 's'}, {"uid-changes", 0, NULL, 'u'}, {"unicode", 0, NULL, 'U'}, @@ -903,11 +917,11 @@ int main(int argc, char **argv) #ifdef WITH_SELINUX while ((c = - getopt_long(argc, argv, "aAcGhH:nplsuUVZ", options, + getopt_long(argc, argv, "aAcGhH:npglsuUVZ", options, NULL)) != -1) #else /*WITH_SELINUX */ while ((c = - getopt_long(argc, argv, "aAcGhH:nplsuUV", options, NULL)) != -1) + getopt_long(argc, argv, "aAcGhH:npglsuUV", options, NULL)) != -1) #endif /*WITH_SELINUX */ switch (c) { case 'a': @@ -953,6 +967,10 @@ int main(int argc, char **argv) pids = 1; compact = 0; break; + case 'g': + pgids = 1; + compact = 0; + break; case 's': show_parents = 1; break; diff --git a/src/timeout.c b/src/timeout.c new file mode 100644 index 0000000..1fe0354 --- /dev/null +++ b/src/timeout.c @@ -0,0 +1,267 @@ +/* + * timout.c Advanced timeout handling for file system calls + * to avoid deadlocks on remote file shares. + * + * Version: 0.1 07-Sep-2011 Fink + * + * Copyright 2011 Werner Fink, 2011 SUSE LINUX Products GmbH, Germany. + * + * This program 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. + * + * Author: Werner Fink <werner@suse.de>, 2011 + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#ifndef USE_SOCKETPAIR +# define USE_SOCKETPAIR 1 +#endif + +#ifdef _FEATURES_H +# error Include local config.h before any system header file +#endif +#include "config.h" /* For _FILE_OFFSET_BITS */ + +#include <errno.h> +#include <pthread.h> +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/stat.h> + +#include <unistd.h> +#if USE_SOCKETPAIR +# include <sys/socket.h> +# include <netdb.h> +# include <netinet/in.h> +# ifndef SHUT_RD +# define SHUT_RD 0 +# endif +# ifndef SHUT_WR +# define SHUT_WR 1 +# endif +# undef pipe +# define pipe(v) (((socketpair(AF_UNIX,SOCK_STREAM,0,v) < 0) || \ + (shutdown((v)[1],SHUT_RD) < 0) || (shutdown((v)[0],SHUT_WR) < 0)) ? -1 : 0) +#endif +#include <wait.h> + +#include "timeout.h" + +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# ifndef destructor +# define destructor __destructor__ +# endif +# ifndef constructor +# define constructor __constructor__ +# endif +# ifndef packed +# define packed __packed__ +# endif +# ifndef inline +# define inline __inline__ +# endif +# ifndef unused +# define unused __unused__ +# endif +# ifndef volatile +# define volatile __volatile__ +# endif +#endif +#ifndef attribute +# define attribute(attr) __attribute__(attr) +#endif + +#if defined __GNUC__ +# undef strcpy +# define strcpy(d,s) __builtin_strcpy((d),(s)) /* Without boundary check please */ +#endif + +/* + * The structure used for communication between the processes + */ +typedef struct _handle { + int errcode; + struct stat argument; + stat_t function; + size_t len; + char path[0]; +} attribute((packed)) handle_t; + +/* + * Using a forked process for doing e.g. stat(2) system call as this + * allows us to send e.g. SIGKILL to this process if it hangs in `D' + * state on a file share due a stalled NFS server. This does not work + * with (p)threads as SIGKILL would kill all threads including main. + */ + +static volatile pid_t active; +static int pipes[4] = {-1, -1, -1, -1}; +static char buf[PATH_MAX + sizeof(handle_t) + 1]; + +static void sigchild(int sig attribute((unused))) +{ + pid_t pid = waitpid(active, NULL, WNOHANG|WUNTRACED); + if (pid <= 0) + return; + if (errno == ECHILD) + return; + active = 0; +} + +static void attribute((constructor)) start(void) +{ + sigset_t sigset, oldset; + struct sigaction act; + ssize_t in; + + if (pipes[1] >= 0) close(pipes[1]); + if (pipes[2] >= 0) close(pipes[2]); + + if (pipe(&pipes[0])) + goto error; + if (pipe(&pipes[2])) + goto error; + + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + act.sa_handler = sigchild; + sigaction(SIGCHLD, &act, 0); + + if ((active = fork()) < 0) + goto error; + + if (active) { + close(pipes[0]); + close(pipes[3]); + pipes[0] = pipes[3] = -1; + return; + } + + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigprocmask(SIG_BLOCK, &sigset, &oldset); + + act.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &act, 0); + + close(pipes[1]); + close(pipes[2]); + dup2(pipes[0], STDIN_FILENO); + dup2(pipes[3], STDOUT_FILENO); + close(pipes[0]); + close(pipes[3]); + pipes[1] = pipes[2] = -1; + pipes[0] = pipes[3] = -1; + + { + handle_t *restrict handle = (void*)&buf[0]; + + while ((in = read(STDIN_FILENO, handle, sizeof(buf))) > sizeof(handle_t)) { + if (handle->function(handle->path, &handle->argument) < 0) + handle->errcode = errno; + write(STDOUT_FILENO, &handle->errcode, sizeof(handle->errcode)+sizeof(handle->argument)); + memset(handle, 0, sizeof(handle_t)); + } + } + sigprocmask(SIG_SETMASK, &oldset, NULL); + exit(0); +error: + if (pipes[0] >= 0) close(pipes[0]); + if (pipes[1] >= 0) close(pipes[1]); + if (pipes[2] >= 0) close(pipes[2]); + if (pipes[3] >= 0) close(pipes[3]); +} + +static void /* attribute((destructor)) */ stop(void) +{ + if (active && waitpid(active, NULL, WNOHANG|WUNTRACED) == 0) + kill(active, SIGKILL); +} + +static sigjmp_buf jenv; +static void sigjump(int sig attribute((unused))) +{ + siglongjmp(jenv, 1); +} + +/* + * External routine + */ +int timeout(stat_t function, const char *path, struct stat *restrict argument, time_t seconds) +{ + handle_t *restrict handle = (void*)&buf[0]; + struct sigaction alrm_act, pipe_act, new_act; + sigset_t sigset, oldset; + + if (active <= 0) /* Oops, last one failed therefore clear status and restart */ + start(); + + memset(handle, 0, sizeof(handle_t)); + handle->len = strlen(path) + 1; + if (handle->len >= PATH_MAX) { + errno = ENAMETOOLONG; + goto error; + } + handle->errcode = 0; + handle->argument = *argument; + handle->function = function; + strcpy(handle->path, path); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigaddset(&sigset, SIGPIPE); + sigprocmask(SIG_UNBLOCK, &sigset, &oldset); + + memset(&new_act, 0, sizeof(new_act)); + sigemptyset(&new_act.sa_mask); + new_act.sa_flags = SA_RESETHAND; + + if (sigsetjmp(jenv, 1)) + goto timed; + + new_act.sa_handler = sigjump; + sigaction(SIGALRM, &new_act, &alrm_act); + sigaction(SIGPIPE, &new_act, &pipe_act); + alarm(seconds); + + write(pipes[1], handle, sizeof(handle_t)+handle->len); + read(pipes[2], &handle->errcode, sizeof(handle->errcode)+sizeof(handle->argument)); + + alarm(0); + sigaction(SIGPIPE, &pipe_act, NULL); + sigaction(SIGALRM, &alrm_act, NULL); + + if (handle->errcode) { + errno = handle->errcode; + goto error; + } + + *argument = handle->argument; + sigprocmask(SIG_SETMASK, &oldset, NULL); + + return 0; +timed: + (void) alarm(0); + sigaction(SIGPIPE, &pipe_act, NULL); + sigaction(SIGALRM, &alrm_act, NULL); + sigprocmask(SIG_SETMASK, &oldset, NULL); + stop(); + errno = ETIMEDOUT; +error: + return -1; +} + +/* + * End of timeout.c + */ diff --git a/src/timeout.h b/src/timeout.h new file mode 100644 index 0000000..546c13b --- /dev/null +++ b/src/timeout.h @@ -0,0 +1,36 @@ +/* + * timout.h Advanced timeout handling for file system calls + * to avoid deadlocks on remote file shares. + * + * Version: 0.1 07-Sep-2011 Fink + * + * Copyright 2011 Werner Fink, 2011 SUSE LINUX Products GmbH, Germany. + * + * This program 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. + * + * Author: Werner Fink <werner@suse.de>, 2011 + */ + +#ifndef _TIMEOUT_H +#define _TIMEOUT_H + +#include "config.h" /* For _FILE_OFFSET_BITS */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <limits.h> + +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# ifndef restrict +# define restrict __restrict__ +# endif +#endif + +typedef int (*stat_t)(const char *, struct stat *restrict); +extern int timeout(stat_t, const char *, struct stat *restrict, time_t); + +#endif |