?? parser.c
字號:
/* * Copyright 1998-2003 by Albert Cahalan; all rights reserved. * This file may be used subject to the terms and conditions of the * GNU Library General Public License Version 2, or any later version * at your option, as published by the Free Software Foundation. * This program 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 Library General Public License for more details. *//* Ought to have debug print stuff like this: * #define Print(fmt, args...) printf("Debug: " fmt, ## args) */#include <stdlib.h>#include <stdio.h>#include <string.h>/* username lookups */#include <sys/types.h>#include <pwd.h>#include <grp.h>#include <sys/stat.h>#include <unistd.h>#include "common.h"#include "../proc/version.h"#define ARG_GNU 0#define ARG_END 1#define ARG_PGRP 2#define ARG_SYSV 3#define ARG_PID 4#define ARG_BSD 5#define ARG_FAIL 6#define ARG_SESS 7static int w_count = 0;static int ps_argc; /* global argc */static char **ps_argv; /* global argv */static int thisarg; /* index into ps_argv */static char *flagptr; /* current location in ps_argv[thisarg] */static int not_pure_unix = 0; /* set by BSD and GNU options */static int force_bsd = 0; /* set when normal parsing fails */#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\ return "The " x " option is exclusive."/********** utility functions **********//* * Both "-Oppid" and "-O ppid" should be legal, though Unix98 * does not require it. BSD and Digital Unix allow both. * Return the argument or NULL; */static const char *get_opt_arg(void){ if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */ not_pure_unix = 1; return flagptr+1; } if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ /* argument follows ps_argv[thisarg] */ if(*(ps_argv[thisarg+1]) == '\0') return NULL; return ps_argv[++thisarg];}/********** parse lists (of UID, tty, GID, PID...) **********/static const char *parse_pid(char *str, sel_union *ret){ char *endp; unsigned long num; static const char pidrange[] = "Process ID out of range."; static const char pidsyntax[] = "Process ID list syntax error."; num = strtoul(str, &endp, 0); if(*endp != '\0') return pidsyntax; if(num<1) return pidrange; if(num > 0x7fffffffUL) return pidrange; ret->pid = num; return 0;}static const char *parse_uid(char *str, sel_union *ret){ struct passwd *passwd_data; char *endp; unsigned long num; static const char uidrange[] = "User ID out of range."; static const char uidexist[] = "User name does not exist."; num = strtoul(str, &endp, 0); if(*endp != '\0'){ /* hmmm, try as login name */ passwd_data = getpwnam(str); if(!passwd_data) return uidexist; num = passwd_data->pw_uid; } if(num > 0xfffffffeUL) return uidrange; ret->uid = num; return 0;}static const char *parse_gid(char *str, sel_union *ret){ struct group *group_data; char *endp; unsigned long num; static const char gidrange[] = "Group ID out of range."; static const char gidexist[] = "Group name does not exist."; num = strtoul(str, &endp, 0); if(*endp != '\0'){ /* hmmm, try as login name */ group_data = getgrnam(str); if(!group_data) return gidexist; num = group_data->gr_gid; } if(num > 0xfffffffeUL) return gidrange; ret->gid = num; return 0;}static const char *parse_cmd(char *str, sel_union *ret){ strncpy(ret->cmd, str, sizeof ret->cmd); // strncpy pads to end return 0;}static const char *parse_tty(char *str, sel_union *ret){ struct stat sbuf; static const char missing[] = "TTY could not be found."; static const char not_tty[] = "List member was not a TTY."; char path[4096]; if(str[0]=='/'){ if(stat(str, &sbuf) >= 0) goto found_it; return missing; }#define lookup(p) \ snprintf(path,4096,p,str); \ if(stat(path, &sbuf) >= 0) goto found_it lookup("/dev/pts/%s"); /* New Unix98 ptys go first */ lookup("/dev/%s"); lookup("/dev/tty%s"); lookup("/dev/pty%s"); lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */ if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */ ret->tty = 0; /* processes w/o tty */ return 0; } if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */ ret->tty = 0; /* processes w/o tty */ return 0; } if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */ ret->tty = 0; /* processes w/o tty */ return 0; }#undef lookup return missing;found_it: if(!S_ISCHR(sbuf.st_mode)) return not_tty; ret->tty = sbuf.st_rdev; return 0;}/* * Used to parse lists in a generic way. (function pointers) */static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){ selection_node *node; char *buf; /* temp copy of arg to hack on */ char *sep_loc; /* separator location: " \t," */ char *walk; int items; int need_item; const char *err; /* error code that could or did happen */ /*** prepare to operate ***/ node = malloc(sizeof(selection_node)); node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */ node->n = 0; buf = malloc(strlen(arg)+1); strcpy(buf, arg); /*** sanity check and count items ***/ need_item = 1; /* true */ items = 0; walk = buf; err = "Improper list."; do{ switch(*walk){ case ' ': case ',': case '\t': case '\0': if(need_item) goto parse_error; need_item=1; break; default: if(need_item) items++; need_item=0; } } while (*++walk); if(need_item) goto parse_error; node->n = items; /*** actually parse the list ***/ walk = buf; while(items--){ sep_loc = strpbrk(walk," ,\t"); if(sep_loc) *sep_loc = '\0'; if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error; walk = sep_loc + 1; /* point to next item, if any */ } free(buf); node->next = selection_list; selection_list = node; return NULL;parse_error: free(buf); free(node->u); free(node); return err;}/***************** parse SysV options, including Unix98 *****************/static const char *parse_sysv_option(void){ const char *arg; const char *err; flagptr = ps_argv[thisarg]; while(*++flagptr){ // Find any excuse to ignore stupid Unix98 misfeatures. // // This list of options is ONLY for those defined by the // "IEEE Std 1003.1, 2004 Edition", "ISO/IEC 9945:2003", // or "Version 2 of the Single Unix Specification". // // It may be time to re-think the existance of this list. // In the meantime, please do not add to it. The list is // intended to ONLY contain flags defined by the POSIX and UNIX // standards published by The Open Group, IEEE, and ISO. if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX switch(*flagptr){ case 'A': trace("-A selects all processes.\n"); all_processes = 1; break; case 'C': /* end */ trace("-C select by process name.\n"); /* Why only HP/UX and us? */ arg=get_opt_arg(); if(!arg) return "List of command names must follow -C."; err=parse_list(arg, parse_cmd); if(err) return err; selection_list->typecode = SEL_COMM; return NULL; /* can't have any more options */ case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */ trace("-F does fuller listing\n"); format_modifiers |= FM_F; format_flags |= FF_Uf; unix_f_option = 1; /* does this matter? */ break; case 'G': /* end */ trace("-G select by RGID (supports names)\n"); arg=get_opt_arg(); if(!arg) return "List of real groups must follow -G."; err=parse_list(arg, parse_gid); if(err) return err; selection_list->typecode = SEL_RGID; return NULL; /* can't have any more options */ case 'H': /* another nice HP/UX feature */ trace("-H Process hierarchy (like ASCII art forest option)\n"); forest_type = 'u'; break;#if 0 case 'J': // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe? trace("-J select by job ID\n"); // want a JID ("jid") for "-j" too arg=get_opt_arg(); if(!arg) return "List of jobs must follow -J."; err=parse_list(arg, parse_jid); if(err) return err; selection_list->typecode = SEL_JID; return NULL; /* can't have any more options */#endif case 'L': /* */ /* In spite of the insane 2-level thread system, Sun appears to * have made this option Linux-compatible. If a process has N * threads, ps will produce N lines of output. (not N+1 lines) * Zombies are the only exception, with NLWP==0 and 1 output line. * SCO UnixWare uses -L too. */ trace("-L Print LWP (thread) info.\n"); thread_flags |= TF_U_L;// format_modifiers |= FM_L; break; case 'M': // typically the SE Linux context trace("-M Print security label for Mandatory Access Control.\n"); format_modifiers |= FM_M; break; case 'N': trace("-N negates.\n"); negate_selection = 1; break; case 'O': /* end */ trace("-O is preloaded -o.\n"); arg=get_opt_arg(); if(!arg) return "Format or sort specification must follow -O."; defer_sf_option(arg, SF_U_O); return NULL; /* can't have any more options */ case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */ trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n"); format_modifiers |= FM_P; break;#if 0 case 'R': // unknown HP/UX feature, like IRIX "-J" maybe? trace("-R select by PRM group\n"); arg=get_opt_arg(); if(!arg) return "List of PRM groups must follow -R."; err=parse_list(arg, parse_prm); if(err) return err; selection_list->typecode = SEL_PRM; return NULL; /* can't have any more options */#endif case 'T': /* IRIX 6.5 docs suggest POSIX threads get shown individually. * This would make -T be like -L, -m, and m. (but an extra column) * Testing (w/ normal processes) shows 1 line/process, not 2. * Also, testing shows PID==SPID for all normal processes. */ trace("-T adds strange SPID column (old sproc() threads?)\n"); thread_flags |= TF_U_T;// format_modifiers |= FM_T; break; case 'U': /* end */ trace("-U select by RUID (supports names).\n"); arg=get_opt_arg(); if(!arg) return "List of real groups must follow -U."; err=parse_list(arg, parse_uid); if(err) return err; selection_list->typecode = SEL_RUID; return NULL; /* can't have any more options */ case 'V': /* single */ trace("-V prints version.\n"); exclusive("-V"); display_version(); exit(0); // This must be verified against SVR4-MP. (UnixWare or Powermax) // Leave it undocumented until that problem is solved. case 'Z': /* full Mandatory Access Control level info */ trace("-Z shows full MAC info\n"); format_modifiers |= FM_M; break; case 'a': trace("-a select all with a tty, but omit session leaders.\n"); simple_select |= SS_U_a; break; case 'c': /* HP-UX and SunOS 5 scheduling info modifier */ trace("-c changes scheduling info.\n"); format_modifiers |= FM_c; break; case 'd': trace("-d select all, but omit session leaders.\n"); simple_select |= SS_U_d; break; case 'e': trace("-e selects all processes.\n"); all_processes = 1; break; case 'f': trace("-f does full listing\n"); format_flags |= FF_Uf; unix_f_option = 1; /* does this matter? */ break; case 'g': /* end */ trace("-g selects by session leader OR by group name\n"); arg=get_opt_arg(); if(!arg) return "List of session leaders OR effective group names must follow -g."; err=parse_list(arg, parse_pid); if(!err){ selection_list->typecode = SEL_SESS; return NULL; /* can't have any more options */ } err=parse_list(arg, parse_gid); if(!err){ selection_list->typecode = SEL_EGID; return NULL; /* can't have any more options */ } return "List of session leaders OR effective group IDs was invalid."; case 'j': trace("-j jobs format.\n"); /* old Debian used RD_j and Digital uses JFMT */ if(sysv_j_format) format_flags |= FF_Uj; else format_modifiers |= FM_j; break; case 'l': trace("-l long format.\n"); format_flags |= FF_Ul; break; case 'm': trace("-m shows threads.\n"); /* note that AIX shows 2 lines for a normal process */ thread_flags |= TF_U_m; break; case 'n': /* end */ trace("-n sets namelist file.\n"); arg=get_opt_arg(); if(!arg) return "Alternate System.map file must follow -n."; namelist_file = arg; return NULL; /* can't have any more options */ case 'o': /* end */ /* Unix98 has gross behavior regarding this. From the following: */ /* ps -o pid,nice=NICE,tty=TERMINAL,comm */ /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */ /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */ /* This parser looks for any excuse to ignore that braindamage. */ trace("-o user-defined format.\n"); arg=get_opt_arg(); if(!arg) return "Format specification must follow -o."; not_pure_unix |= defer_sf_option(arg, SF_U_o); return NULL; /* can't have any more options */ case 'p': /* end */ trace("-p select by PID.\n"); arg=get_opt_arg(); if(!arg) return "List of process IDs must follow -p."; err=parse_list(arg, parse_pid); if(err) return err; selection_list->typecode = SEL_PID;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -