/************************************************** * Hacked by: Moreno "baro" Baricevic * * Contact: * * Date: 19 Oct 2003 * * Title: ompsinfo.c * *================================================* * Prev Modified: (devel Oct-Nov 2003) * * Prev Modified: 30 Nov 2003 v1.1.0-2 * * (openMosix tools 0.3.5 release) * * Prev Modified: 08 Dec 2003 v1.1.0-3 * * Last Modified: 09 May 2004 v1.1.1 * **************************************************/ /* ** This program was originally based on "minimal.c", owned by ** procps 3.1.13 package: ** ** http://procps.sourceforge.net/procps-3.1.13.tar.gz ** ** Some functionalities have been removed, others have been added. ** ** In order to compile use: ** ** gcc -Wall -Wimplicit -Wcast-qual -Wcast-align -Wwrite-strings -Wnested-externs -Wlong-long -Wmissing-declarations -Wmissing-prototypes -Wshadow -Wstrict-prototypes ompsinfo.c -o ompsinfo ** ** or simply: ** ** gcc -Wall ompsinfo.c -o ompsinfo ** ** Purpose of this program is to give a user-friendly interface to some proc ** openMosix files, like "where", "nmigs", "lock" and "cantmove". ** ** Some of our cluster's users need information about the behaviour of their ** programs over openMosix architecture. Can be migrated or why not? How many ** times is migrated and where it's running? ** ** First version of this program was written as a bash script. It works well, ** but in a loop it is VERY slow (obviously). ** So here it is, fast and simple... at least the first version ;-) ** ** "mps" program gives only "where" info. I've hacked mps in order to give ** "nmigs" info, but "cantmove" cannot be peacefully integrated with "ps" ** formatted output. (But now is possible, both in mps and here ;) ** ** Where possible, this program mantains the compatibility with 'ps' Unix98 ** options. ** ** Sorry for my awful english, feel free to correct any kind of error you may ** find. ** ** Please send bug reports, suggestions, insults, to */ /* ** Following are the original comments from "minimal.c". ** Note that this file is distributed under the same terms reported ** below. */ /* * Copyright 1998 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. */ /* This is a minimal /bin/ps, designed to be smaller than the old ps * while still supporting some of the more important features of the * new ps. (for total size, note that this ps does not need libproc) * It is suitable for Linux-on-a-floppy systems only. * * Maintainers: do not compile or install for normal systems. * Anyone needing this will want to tweak their compiler anyway. */ ////////////////////////////////////////////////////////////////////// // OMPSINFO // ////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* HZ */ #include /* PAGE_SIZE */ #define PROGRAM_NAME "ompsinfo" #define PROGRAM_VERSION "1.1.1" #define fakeconst /* const */ /********************************************************************* ** Global variables setted by stat2proc */ static uid_t P_uid; /* RUID as default, EUID if specified by option */ static pid_t P_pid; static char P_cmd[16]; static char P_state; static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid; static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value; static unsigned long P_start_time, P_vsize; static long P_rss; static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch; static unsigned long P_wchan, P_nswap, P_cnswap; /********************************************************************* ** Command line options related variables */ static pid_t want_one_pid = 0; /* also used as flag, must be 0 here (not -1) */ static const char * want_one_command = NULL; /* also used as flag, may be NULL or something */ static const char * want_one_user = NULL; /* also used as flag, may be NULL or something */ static uid_t want_one_uid = -1; /* can be >= 0 */ static char ps_format = '\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] */ #define FALSE 0 #define TRUE (!FALSE) typedef unsigned short int flag_t; /* just TRUE or FALSE */ #define flag_t flag_t typedef struct { flag_t all ; /* -A */ flag_t add ; /* -a */ flag_t by_cmd ; /* -C */ flag_t by_pid ; /* -p */ flag_t by_username ; /* -u */ flag_t by_EUID ; /* -U */ flag_t only_pid ; /* -o */ flag_t long_format ; /* -l */ flag_t no_header ; /* -h */ flag_t page_header ; /* -H */ flag_t debug ; /* -d */ flag_t negate ; /* -N */ } opt_s; #define opt_s opt_s static opt_s opt_flag = { FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE , FALSE }; /********************************************************************* ** usage wrapper, warnings and diagnostics */ #define USAGE( args... ) usage( args ) //#define USAGE( args... ) usage( NULL ) /* -2KB ~ */ //#define USAGE( args... ) usage( "" ) /* -1.9KB ~ but preserves correct exit status */ #define BELL { if ( isatty( STDERR_FILENO ) ) fprintf( stderr , "\a" ); } #if ! defined EXIT_SUCCESS || EXIT_SUCCESS != 0 /* stdlib.h */ # warning redefining EXIT_SUCCESS and EXIT_FAILURE # undef EXIT_SUCCESS # undef EXIT_FAILURE # define EXIT_SUCCESS 0 /* exit status OK */ # define EXIT_FAILURE (!EXIT_SUCCESS) /* exit status !OK */ //# define EXIT_ERROR (-EXIT_FAILURE) /* error condition (-1) */ #endif /********************************************************************* ** NEGATE */ /* try to use only one flag for all the "negate" arguments... %-) */ #define USE_NEGOPT /* flags */ #ifdef USE_NEGOPT # define NEGNUT 0x00 /* 0000 */ # define NEGALL 0x01 /* 0001 */ # define NEGCMD 0x02 /* 0010 */ # define NEGUSR 0x04 /* 0100 */ # if 0 /* not implemented and probably will never be */ # define NEGPID 0x04 /* 1000 */ # ifdef NEGPID # define NEGMSK ( NEGCMD | NEGUSR | NEGPID ) /* 1110 */ # endif # endif #endif /* USE_NEGOPT */ /* negate wrappers */ #ifdef USE_NEGOPT # define _NEGATE( cond ) negate( cond , NEGALL ) # define _NEGOPT( cond , flag ) negate( cond , flag ) #else # define _NEGATE( cond ) negate( cond ) # define _NEGOPT( cond , flag ) cond #endif /********************************************************************* ** DEBUG stuff */ #ifdef DEBUG /* useless... +6KB ~ */ //# define DBGprint( fmt , args... ) { if ( opt_flag.debug ) { fprintf( stderr , fmt , ## args ); } } //# define DBGprint( fmt , args... ) { if ( opt_flag.debug ) { fprintf( stderr , "[%s:%d]" fmt , __FILE__ , __LINE__ , ## args ); } } # define DBGprint( fmt , args... ) { if ( opt_flag.debug ) { fprintf( stderr , "[%s:%4d][%-12.12s] " fmt , __FILE__ , __LINE__ , __FUNCTION__ , ## args ); } } #else # define DBGprint( args... ) {} #endif /********************************************************************* ** System */ #ifndef PAGE_SIZE # warning PAGE_SIZE not defined, assuming it is 4096 # define PAGE_SIZE 4096 #endif #ifndef HZ # warning HZ not defined, assuming it is 100 # define HZ 100 #endif #define BASE_PRIORITY 60 /********************************************************************* ** CANTMOVE MESSAGES & FLAGS */ #define MIGRATABLE "migratable" #define MIG_LOCKED MIGRATABLE " but locked" #define NOT_AVAIL "N/A" #define MIGRATABLE_FLAG '0' #define NOT_AVAIL_FLAG '-' #define CANTMOVE_FLAG '1' //#define MIG_LOCKED_FLAG 'L' /* lock, mig & lock, nomig & lock ??? Isn't clear enough. */ /* ** NOTE: CANTFLAG is '0' or '1' but, as char, may be Y|N, M|N|L, ... */ /********************************************************************* ** Functions declaration (huge amount of 'const' is useful only for me...) */ //#warning "FIXME: update functions declaration" /* usage */ static void usage ( const char * errmsg , ... ); static void help ( void ); static void version ( void ); /* cmdline options parsing */ static void parse_pid ( const char * str ); static fakeconst uid_t get_uid ( const char * username ); static const char * get_opt_arg ( void ); static void parse_sysv_option ( void ); static void arg_parse ( int argc , char * argv[] ); /* proc data acquisition */ static fakeconst int stat2proc ( const pid_t pid ); static const char * do_time ( unsigned long t ); static const char * do_user ( const uid_t uid ); static fakeconst int readint ( const pid_t pid , const char * file ); static const char * readcantmove ( const pid_t pid , char * cantflag ); /* screen size */ static fakeconst int get_screen_rows ( void ); /* negate */ #ifdef USE_NEGOPT static fakeconst int negate ( int condition , const int flag ); static void parse_negopt ( const char * ptr ); #else static fakeconst int negate ( const int condition ); #endif /* openMosix */ static fakeconst int msx_is_mosix ( void ); /* ??? */ int main ( int argc , char * argv[] ); /************************************************************************************** #################################### oMFS CHECK #################################### ***************************************************************************************/ #define USE_OMFS_CHECK #ifdef USE_OMFS_CHECK /* ** I will use MFS as 3charslength label while the filesystem is oMFS, forgive me... */ /********************************************************************* ** MACRO, FLAGS and TYPES */ #define PROC_MOUNTS "/proc/mounts" #define MTAB_SEP " \t" #define MTAB_EOL "\n" #define OMFS_TYPE "mfs" #define BUFSIZE 1024 /* fgets(mounts), readlink(cwd), strncmp(mounts,cwd) */ typedef struct { flag_t req; /* is it requested by user? */ const char * mpt; /* oMFS mount point */ size_t len; /* length of the oMFS mount point string */ } oMfs_s ; #define oMfs_s oMfs_s oMfs_s omfs = { FALSE , NULL , 0 }; /********************************************************************* ** Functions declaration */ //#warning "FIXME: update functions declaration" static char * parse_mtab_line ( char * line ); /* argument will be modified */ static void get_omfs_mp ( void ); static const char * cwd_is_omfs ( const pid_t pid ); /****************************************************************************** *_____________________________ PARSE_MTAB_LINE _____________________________* ******************************************************************************/ /* ** argument must be a mtab/mounts line: ** [device mountpoint fstype options flags] ** if the filesystem type is oMFS, returns the oMFS mount point */ #if defined (__STDC__) static char * parse_mtab_line ( char * line ) #else static char * parse_mtab_line ( line ) char * line ; #endif /* __STDC__ */ { #ifdef DEBUG char * dev , * ops , * fls ; /* device , options , dump & fsck flags */ #endif char * mpt , * fst ; /* mount point , filesystem type */ /* don't mind if 'line' is modified by strtok */ #ifdef DEBUG dev = strtok( line , MTAB_SEP ); #else (void)strtok( line , MTAB_SEP ); #endif mpt = strtok( NULL , MTAB_SEP ); fst = strtok( NULL , MTAB_SEP ); #ifdef DEBUG ops = strtok( NULL , MTAB_SEP ); fls = strtok( NULL , MTAB_EOL ); DBGprint( "dev = [%s]\n" , dev ); /* fs_spec */ DBGprint( "mpt = [%s]\n" , mpt ); /* fs_file */ DBGprint( "fst = [%s]\n" , fst ); /* fs_vfstype */ DBGprint( "ops = [%s]\n" , ops ); /* fs_mntops */ DBGprint( "fls = [%s]\n" , fls ); /* fs_freq (dump) , fs_passno (fsck) */ #endif if ( ! fst || strcmp( fst , OMFS_TYPE ) ) return NULL; return strdup( mpt ); } /* parse_mtab_line */ /************************************************************************** *_____________________________ GET_OMFS_MP _____________________________* **************************************************************************/ /* ** get oMFS mount point from /proc/mounts (if mounted...) */ #if defined (__STDC__) static void get_omfs_mp ( void ) #else static void get_omfs_mp ( void ) #endif /* __STDC__ */ { FILE * stream; char line[BUFSIZE]; char * mpoint = NULL; DBGprint( "get oMFS mount point from %s\n" , PROC_MOUNTS ); if ( ! ( stream = fopen( PROC_MOUNTS , "r" ) ) ) return; #define READLINE ( \ memset( line , 0 , sizeof( line ) ) && \ fgets( line , sizeof( line ) - 1 , stream ) \ ) while ( READLINE ) { DBGprint( "line: %s" , line ); mpoint = parse_mtab_line( line ); /* 'line' will be modified by strtok */ if ( mpoint ) break; } #undef READLINE /* ** There's need to check fstype ( if dev=="cluster " && fstype==" mfs " ) ** or "cluster " is used only by (o)mfs? */ fclose( stream ); if ( mpoint ) { omfs.mpt = mpoint; omfs.len = strlen( omfs.mpt ); DBGprint( "got oMFS mpt: %s [%d]\n" , omfs.mpt , omfs.len ); } else { DBGprint( "oMFS mount point not found!\n" ); } } /* get_omfs_mp */ /************************************************************************** *_____________________________ CWD_IS_OMFS _____________________________* **************************************************************************/ /* ** check if a process is running on oMFS path (looking at symlink /proc//cwd) */ #if defined (__STDC__) static const char * cwd_is_omfs ( const pid_t pid ) #else static const char * cwd_is_omfs ( pid ) const pid_t pid; #endif /* __STDC__ */ { static char filename[80]; static char cwd_buf[BUFSIZE]; static char omfs_node[10]; int rl = 0; DBGprint( "check if process %u is running on oMFS...\n" , pid ); if ( ! omfs.mpt || ! omfs.len ) return "-"; /* no oMFS ? */ memset( cwd_buf , 0 , sizeof( cwd_buf ) ); sprintf( filename , "/proc/%u/cwd" , pid ); rl = readlink( filename , cwd_buf , sizeof( cwd_buf ) ); if ( rl == -1 ) return "?"; /* A zombie may have cwd pointing to nowhere */ /* ** If destination is removed, cwd is a dead link but the ** kernel marks it as "/path/to/removed/dir/ (deleted)" */ DBGprint( "cwd = [%s]\n" , cwd_buf ); if ( strncmp( cwd_buf , omfs.mpt , omfs.len ) ) return "/"; /* local, not on oMFS */ /* at least $cwd ~= m|^$mpt| */ { const char * ptr = ( cwd_buf + omfs.len ); /* use cwd but skip mpt path */ int node = 0; DBGprint( "cwd_buf [%s] -> mpt [%s] -> overmpt [%c]\n" , cwd_buf , omfs.mpt , *ptr ); if ( ! *ptr ) return "="; /* cwd == mpt */ if ( sscanf( ptr , "/%d" , &node ) == 1 ) snprintf( omfs_node , sizeof( omfs_node ) , "%d" , node ); else return "??"; /* means /mfs/[^0-9]*, limbo... */ } return omfs_node; } /* cwd_is_omfs */ # undef BUFSIZE #endif /* USE_OMFS_CHECK */ /************************************************************************************** #################################### /oMFS CHECK #################################### ***************************************************************************************/ /************************************************************************************************* #################################### USER OUTPUT SECTION #################################### *************************************************************************************************/ /********************************************************************* ** MACRO, FLAGS and TYPES */ #define SPC ' ' #define NL '\n' #define DELIM ',' #define REMOVE '-' #define OVERWRITE '=' typedef union { int i; const char * s; } union_t; #define union_t union_t #define IS_INT 1 #define IS_STR (!IS_INT) #define IS_CHR IS_INT typedef struct { int show; const char * head; const char * fmt; int isint; union_t val; } info_s; #define info_s info_s /********************************************************************* ** Functions declaration */ //#warning "FIXME: update functions declaration" /* output format */ static void __show_reset ( info_s * i ); static void __show_set ( info_s * i , const int * toset ); static void __show_remove ( info_s * i , const int el ); static void __show_add ( info_s * i , const int el ); static void __set_output ( const char outfmt ); static void __print_header ( info_s * i ); static void __print_procinfo ( info_s * i ); #if 0 static size_t read_cmdline ( char * dst , const size_t sz , const pid_t pid ); static void dummy ( void ); #endif static void __set_procinfo ( void ); static const char * get_delim ( const char * str , const char delim ); static void __add_user_output ( const char * string ); /********************************************************************* ** Global variables */ const char * user_output = NULL; /* maybe filled later by --add|-a */ /* output fields order */ enum { E_FIRST = -1, /* control flag */ E_USER = 0, E_CMD, E_PID, E_PPID, E_PRI, E_NI, E_S, E_NODE, E_NMIGS, #ifdef USE_OMFS_CHECK E_MFS, #endif E_LOCK, E_CANTF, /* cantmove as flag */ E_CANTM, /* cantmove as string */ E_TIME, E_COMMAND, E_LAST /* control flag */ /* aliases */ ,E_NICE = E_NI ,E_STAT = E_S ,E_WHERE = E_NODE ,E_CNTM = E_CANTF #ifdef USE_OMFS_CHECK ,E_OMFS = E_MFS #endif }; /* !!! SAME ORDER OF E_* !!! */ static info_s info[] = { { 0 , "USER " , "%-8.8s" , IS_STR , { 0 } } , // username { 0 , "CMD " , "%-16.16s" , IS_STR , { 0 } } , // command_________ { 0 , " PID" , "%5d" , IS_INT , { 0 } } , // 12345 { 0 , " PPID" , "%5d" , IS_INT , { 0 } } , // 12345 { 0 , "PRI" , "%3d" , IS_INT , { 0 } } , // _69 { 0 , " NI" , "%3d" , IS_INT , { 0 } } , // -20 { 0 , "S" , "%c" , IS_CHR , { 0 } } , // a { 0 , "NODE" , "%4d" , IS_INT , { 0 } } , // 1234 { 0 , "NMIGS" , "%5d" , IS_INT , { 0 } } , // 12345 #ifdef USE_OMFS_CHECK { 0 , "MFS" , "%3s" , IS_STR , { 0 } } , // _ab #endif { 0 , "LOCK" , "%4d" , IS_INT , { 0 } } , // ___1 { 0 , "CNTM" , "%4c" , IS_CHR , { 0 } } , // ___a { 0 , "CANTMOVE" , "%-8s" , IS_STR , { 0 } } , // ............ { 0 , " TIME" , "%11s" , IS_STR , { 0 } } , // dd-hh:mm:ss { 0 , "COMMAND" , "%s" , IS_STR , { 0 } } , // command_________ { 0 , NULL , NULL , 0 , { 0 } } }; /* infos */ /* ========== OUTPUT FORMATS ========== ** default: 'CMD PID NODE NMIGS LOCK CANTMOVE' ** by_pid: 'CMD NODE NMIGS LOCK CANTMOVE' ** by_cmd: ' PID NODE NMIGS LOCK CANTMOVE' ** long: 'USER PID PPID PRI NI S NODE NMIGS LOCK CNTM TIME COMMAND' ** ** all: 'USER' + FORMAT ** negate: 'USER' + 'CMD' + FORMAT ** ** 'MFS' field is added only by "-a mfs" (or "-a omfs"), ifdef USE_OMFS_CHECK... */ enum { O_FIRST = -1, /* control flag */ O_DFLT = 0, O_PID, O_CMD, O_LONG, O_LAST /* control flag */ }; static int output[O_LAST][E_LAST] = { /* pointer to O_LAST output arrays of E_LAST elements */ { E_CMD , E_PID , E_NODE , E_NMIGS , E_LOCK , E_CANTM , E_LAST }, { E_CMD , E_NODE , E_NMIGS , E_LOCK , E_CANTM , E_LAST }, { E_PID , E_NODE , E_NMIGS , E_LOCK , E_CANTM , E_LAST }, { E_USER , E_PID , E_PPID , E_PRI , E_NI , E_S , E_NODE , E_NMIGS , E_LOCK , E_CANTF , E_TIME , E_COMMAND , E_LAST }, }; /* outputs */ static int negate_fields[] = { E_USER , E_CMD , /* E_PID , */ E_LAST }; /* E_PID useless if -Np is not implemented... */ /********************************************************************* ** ...some meaningful comment here, any idea? :) */ #define __IS_SET( field ) ( ( info + field )->show ) /*************************************************************************** *_____________________________ __SHOW_RESET _____________________________* ***************************************************************************/ /* ** total reset */ #if defined (__STDC__) static void __show_reset ( info_s * i ) #else static void __show_reset ( i ) info_s * i ; #endif /* __STDC__ */ { int el = E_FIRST; while( ++el < E_LAST ) { DBGprint( "rst %2d, %d " , el , ( i + el )->show ); ( i + el )->show = 0; DBGprint( "-> %d\n" , ( i + el )->show ); } } /* __show_reset */ /************************************************************************* *_____________________________ __SHOW_SET _____________________________* *************************************************************************/ /* ** massive addiction through int array */ #if defined (__STDC__) static void __show_set ( info_s * i , const int * toset ) #else static void __show_set ( i , toset ) info_s * i ; const int * toset ; #endif /* __STDC__ */ { const int * p = toset; while( *p < E_LAST ) { DBGprint( "set %2d, %d " , *p , ( i + *p )->show ); ( i + *p )->show = 1; DBGprint( "-> %d\n" , ( i + *p )->show ); ++p; } } /* __show_set */ /**************************************************************************** *_____________________________ __SHOW_REMOVE _____________________________* ****************************************************************************/ /* ** single reset through element ** (I don't use a macro because I will use a pointer to this function) */ #if defined (__STDC__) static void __show_remove ( info_s * i , const int el ) #else static void __show_remove ( i , el ) info_s * i ; const int el ; #endif /* __STDC__ */ { DBGprint( "removing info %d\n" , el ); ( i + el )->show = 0; } /* __show_remove */ /************************************************************************* *_____________________________ __SHOW_ADD _____________________________* *************************************************************************/ /* ** single addiction through element ** (I don't use a macro because I will use a pointer to this function) */ #if defined (__STDC__) static void __show_add ( info_s * i , const int el ) #else static void __show_add ( i , el ) info_s * i ; const int el ; #endif /* __STDC__ */ { DBGprint( "adding info %d\n" , el ); ( i + el )->show = 1; } /* __show_add */ /*************************************************************************** *_____________________________ __SET_OUTPUT _____________________________* ***************************************************************************/ /* ** set the output fields to print */ #if defined (__STDC__) static void __set_output ( const char outfmt ) #else static void __set_output ( outfmt ) const char outfmt ; #endif /* __STDC__ */ { DBGprint( "set header for '%c'\n" , isprint( outfmt ) ? outfmt : '0' + outfmt ); /* output set is still empty (but may change, that's why __show_reset() is not here) */ switch( outfmt ) { default: /* can't happen */ case 0: __show_set( info , output[O_DFLT] ); break; case 'p': __show_set( info , output[O_PID ] ); break; case 'C': __show_set( info , output[O_CMD ] ); break; case 'l': __show_set( info , output[O_LONG] ); break; case 'o': __show_add( info , E_PID ); break; /* only PID needs to be displayed */ } } /* set_output */ /***************************************************************************** *_____________________________ __PRINT_HEADER _____________________________* *****************************************************************************/ /* ** print the output header */ #if defined (__STDC__) static void __print_header ( info_s * i ) #else static void __print_header ( i ) info_s * i ; #endif /* __STDC__ */ { info_s * p = i; if ( opt_flag.no_header ) return; DBGprint( "print header\n" ); while ( p->head != NULL ) { if ( p->show ) printf( p->head ) , putchar( SPC ); ++p; } putchar( NL ); } /* __print_header */ /******************************************************************************* *_____________________________ __PRINT_PROCINFO _____________________________* *******************************************************************************/ /* ** Print info about a process. */ #if defined (__STDC__) static void __print_procinfo ( info_s * i ) #else static void __print_procinfo ( i ) info_s * i ; #endif /* __STDC__ */ { info_s * p = i; DBGprint( "print proc info\n" ); while ( p->fmt != NULL ) { if ( p->show ) { if ( p->isint ) printf( p->fmt , p->val.i ); else printf( p->fmt , p->val.s ); putchar( SPC ); } ++p; } putchar( NL ); } /* __print_procinfo */ #ifdef USE_CMDLINE /*************************************************************************** *_____________________________ READ_CMDLINE _____________________________* ***************************************************************************/ char dest[1024]; /* ** From procps-3.1.13/proc/readproc.c */ #if defined (__STDC__) static size_t read_cmdline ( char * dst , const size_t sz , const pid_t pid ) #else static size_t read_cmdline ( dst , sz , pid ) char * dst ; const size_t sz ; const pid_t pid ; #endif /* __STDC__ */ { char name[32]; int fd; size_t n = 0; dst[0] = '\0'; snprintf( name , sizeof name , "/proc/%u/cmdline" , pid ); fd = open( name , O_RDONLY ); if ( fd == -1 ) return 0; for( ; ; ) { ssize_t r = read( fd , dst + n , sz - n ); if ( r == -1 ) { if ( errno == EINTR ) continue; break; } n += r; if ( n == sz ) break; // filled the buffer if ( r == 0 ) break; // EOF } close( fd ); if ( n ) { int i; if ( n == sz ) n--; dst[n] = '\0'; i = n; while ( i-- ) { int c = dst[i]; if ( c < ' ' || c > '~' ) // ! isalpha( c ) dst[i]=' '; } } return n; } /* read_cmdline */ #if defined (__STDC__) static void dummy ( void ) #else static void dummy ( void ) void ; #endif /* __STDC__ */ { read_cmdline( dest , sizeof dest , getpid() ); if ( 0 ) dummy(); } #endif /***************************************************************************** *_____________________________ __SET_PROCINFO _____________________________* *****************************************************************************/ /* ** store the info acquired by stat2proc and read* to the output structure */ #if defined (__STDC__) static void __set_procinfo ( void ) #else static void __set_procinfo ( void ) #endif /* __STDC__ */ { /* ** Yes, I know. This code will be executed for each ** process even if not all the fields are requested. ** ** If I don't need a field is correct to get that info anyway ? ** With cantmove but not lock, the MIG_LOCKED check fails or we ** lost a part of the info... ** We read all, if someone complains about it, uncomment next line. */ #define SKIP_INFO int oM_where = -1, oM_nmigs = -1, oM_lock = -1; const char * oM_cantmove = NULL; char oM_cantflag = NOT_AVAIL_FLAG; DBGprint( "setting info\n" ); #ifdef SKIP_INFO if ( __IS_SET( E_WHERE ) ) #endif { oM_where = readint( P_pid , "where" ); } #ifdef SKIP_INFO if ( __IS_SET( E_NMIGS ) ) #endif { oM_nmigs = readint( P_pid , "nmigs" ); } #ifdef SKIP_INFO if ( __IS_SET( E_LOCK ) ) #endif { oM_lock = readint( P_pid , "lock" ); } #ifdef SKIP_INFO if ( __IS_SET( E_CANTM ) || __IS_SET( E_CANTF ) ) #endif { oM_cantmove = readcantmove( P_pid , &oM_cantflag ); #ifdef MIG_LOCKED # ifdef SKIP_INFO if ( __IS_SET( E_LOCK ) ) /* we lost "but locked" on "migratable" */ # endif { if ( oM_lock && oM_cantflag == MIGRATABLE_FLAG ) { oM_cantmove = MIG_LOCKED; // oM_cantflag = MIG_LOCKED_FLAG; } } #endif } info[E_USER ].val.s = do_user( P_uid ); info[E_CMD ].val.s = P_cmd; info[E_PID ].val.i = P_pid; info[E_PPID ].val.i = P_ppid; info[E_PRI ].val.i = ( BASE_PRIORITY + (int)P_priority ); info[E_NI ].val.i = (int)P_nice; info[E_S ].val.i = P_state; info[E_NODE ].val.i = oM_where; info[E_NMIGS ].val.i = oM_nmigs; #ifdef USE_OMFS_CHECK if ( omfs.req ) /* never computed unless requested */ info[E_MFS ].val.s = cwd_is_omfs( P_pid ); #endif info[E_LOCK ].val.i = oM_lock; info[E_CANTF ].val.i = oM_cantflag; info[E_CANTM ].val.s = oM_cantmove; info[E_TIME ].val.s = do_time( P_utime + P_stime ); info[E_COMMAND].val.s = P_cmd; } /* __set_procinfo */ /************************************************************************** *_____________________________ __GET_DELIM _____________________________* **************************************************************************/ /* ** returns the pointer to the next occurrence of delim in the given string, ** or NULL if delim not found or no more tokens left. DOES NOT modify the ** input string. */ #if defined (__STDC__) static const char * get_delim ( const char * str , const char delim ) #else static const char * get_delim ( str , delim ) const char * str ; const char delim ; #endif /* __STDC__ */ { const char * ptr = str; DBGprint( "token '%c' from [%s]\n" , delim , str ); if ( ! *ptr ) return NULL; while( ptr && *ptr ) { if ( *ptr == delim ) return ( *++ptr ) ? ptr : NULL; /* returns NULL if trailing delim, next token otherwise */ else ++ptr; } return NULL; /* no more tokens */ } /* __get_delim */ /******************************************************************************** *_____________________________ __ADD_USER_OUTPUT _____________________________* ********************************************************************************/ /* ** parse the -a|--add arguments defining the user's output format request, and ** add or remove fields from the output format setted by other options */ #if defined (__STDC__) static void __add_user_output ( const char * string ) #else static void __add_user_output ( string ) const char * string ; #endif /* __STDC__ */ { #define DOT " o " #define NOP " " #ifdef USE_OMFS_CHECK # define OMFS_ARG ",(Mfs|Omfs)" #else # define OMFS_ARG "" #endif const char * helpmesg = "--add help\n" DOT " arguments: User,CMd,PId,PPid,PRiority,NIce,Stat,(NOde,Where),NMigs" OMFS_ARG ",Lock,(CANTFlag|CNtm),CANTMove,Time,COmmand\n" NOP " use at least the upper case letters reported above, only those letters are parsed.\n" DOT " String is not case sensitive.\n" DOT " DELIMITER is \",\".\n" DOT " Valid strings can be \"user,cmd,pid\", \"u,cm,pi\". Order has no influence.\n" DOT " \"cmd\" and \"command\" reports the same information but on different position.\n" DOT " \"time\" and \"command\": \"cantmove\" can mess up these columns, use \"cantflag\" instead.\n" DOT " '-' remove following info (--add \"user,-cmd,-pid,command\").\n" DOT " '=' as first character set the header exactly as defined by your argument (--add \"=user,cmd,pid\")." ; const char * arg = string; char f , s; /* first and second char for the argument, used by switch */ int c = 0; /* count the tokens (debug only) */ void (* __show_hndlr)( info_s * , const int ); /* handler to 'add' or 'remove' function */ #if 1 #define T_F DBGprint( "1st [%c]\n" , f ) #define T_S DBGprint( "2nd [%c]\n" , s ) #else #define T_F #define T_S #endif #define IS_RESERVED( char ) ( char == DELIM || char == REMOVE || char == OVERWRITE ) if ( ! *arg ) USAGE( "--add " ); DBGprint( "parsing string [%s]\n" , string ); /* avoid "-a [,-=]" */ if ( IS_RESERVED( *arg ) && ! *( arg + 1 ) ) USAGE( "--add: useless '%c'" , *arg ); /* avoid worst error later */ while( *arg ) if ( isalpha( *arg ) || IS_RESERVED( *arg ) ) ++arg; else USAGE( "--add: invalid character '%c' in string (allowed chars [a-zA-Z,-=])" , ( isprint( *arg ) ? *arg : '?' ) ); #undef IS_RESERVED arg = string; if ( *arg == OVERWRITE ) ++arg , __show_reset( info ); do { DBGprint( "arg = %s\n" , arg ); if ( *arg == REMOVE ) ++arg , __show_hndlr = __show_remove; else __show_hndlr = __show_add; f = tolower( *arg ); s = tolower( *( arg + 1 ) ); switch( f ) { case 'h': USAGE( helpmesg ); case DELIM: break; case 'c':T_F; switch( s ) { case 'a':T_S; if ( ! strncasecmp( arg , "cantf" , 5 ) ) ++c , __show_hndlr( info , E_CANTF ); else if ( ! strncasecmp( arg , "cantm" , 5 ) ) ++c , __show_hndlr( info , E_CANTM ); else USAGE( "--add 'ca': CANTMove|CANTFlag (invalid arg '%s')" , arg ); break; case 'n':T_S; ++c , __show_hndlr( info , E_CNTM ); break; /* alias for cantflag */ case 'm':T_S; ++c , __show_hndlr( info , E_CMD ); break; case 'o':T_S; ++c , __show_hndlr( info , E_COMMAND ); break; default: USAGE( "--add 'c': CANTMove|(CANTFlag|CNtm)|CMd|COmmand (invalid arg '%s')" , arg ); } break; case 'l':T_F; ++c , __show_hndlr( info , E_LOCK ); break; #ifdef USE_OMFS_CHECK case 'o': case 'm':T_F; ++c , __show_hndlr( info , E_MFS ); break; #endif case 'n':T_F; switch( s ) { case 'i':T_S; ++c , __show_hndlr( info , E_NI ); break; /* nice */ case 'm':T_S; ++c , __show_hndlr( info , E_NMIGS ); break; case 'o':T_S; ++c , __show_hndlr( info , E_NODE ); break; /* where */ default: USAGE( "--add 'n': NIce|NMigs|NOde (invalid arg '%s')" , arg ); } break; case 'p':T_F; switch( s ) { case 'i':T_S; ++c , __show_hndlr( info , E_PID ); break; case 'p':T_S; ++c , __show_hndlr( info , E_PPID ); break; case 'r':T_S; ++c , __show_hndlr( info , E_PRI ); break; default: USAGE( "--add 'p': PId|PPid|PRi (invalid arg '%s')" , arg ); } break; case 's':T_F; ++c , __show_hndlr( info , E_S ); break; /* state */ case 't':T_F; ++c , __show_hndlr( info , E_TIME ); break; case 'u':T_F; ++c , __show_hndlr( info , E_USER ); break; case 'w':T_F; ++c , __show_hndlr( info , E_NODE ); break; default: USAGE( helpmesg ); } /* switch */ } while ( ( arg = get_delim( arg , DELIM ) ) ); DBGprint( "%d tokens extracted\n" , c ); #undef T_F #undef T_S } /* __add_user_output */ /************************************************************************************************* #################################### /USER OUTPUT SECTION #################################### *************************************************************************************************/ /******************************************************************** *_____________________________ USAGE _____________________________* ********************************************************************/ /* ** needs explanation? */ #if defined (__STDC__) static void usage ( const char * errmsg , ... ) #else static void usage ( errmsg ) const char * errmsg ; #endif /* __STDC__ */ { if ( errmsg && *errmsg ) { va_list args; BELL; fprintf( stderr , "*** Usage error: " ); va_start( args , errmsg ); vfprintf( stderr , errmsg , args ); va_end( args ); fprintf( stderr , "\n" ); } fprintf( stderr , "\n" "Usage: %s OPTIONS ...\n" "\t[-A] [-C ] [-p ] [-u ] [-U]\n" #ifdef USE_NEGOPT "\t[-N] [-NC ] [-Nu ]\n" #else "\t[-N]\n" #endif "\t[-l] [-o ] [-a|--add INFO,...] [-h] [-H]\n" "\t[-V|--version] [--help] [--usage]\n" "\n" , PROGRAM_NAME ); exit( errmsg ? EXIT_FAILURE : EXIT_SUCCESS ); } /* usage */ /******************************************************************* *_____________________________ HELP _____________________________* *******************************************************************/ /* ** needs explanation? */ #if defined (__STDC__) static void help ( void ) #else static void help ( void ) #endif /* __STDC__ */ { fprintf( stderr , "\n" "Usage: %s [-C |-p |] [-u |-A] [-U] [-N] [-l|-o pid=] [-a INFO,...] [-h|-H]\n" "\n" " *Selection*\n" " -A select all users and processes (add USER header to format)\n" " -C select by command name (only accepts one)\n" " -p select by process ID (only accepts one)\n" " -u select by owner (only accepts one)\n" "\n" " *Modifier*\n" " -U use effective user ID instead of real user ID\n" "\n" " *Weird Modifiers*\n" " -N negate \"composed\" selection\n" #ifdef USE_NEGOPT " -NC negate command selection (not UX98, != \"-N -C ...\")\n" " -Nu negate user selection (not UX98, != \"-N -u ...\")\n" #endif "\n" " *Format*\n" " -l long format\n" " -o user-defined format (limited support, only \"-o pid=\")\n" " -a|--add INFO,... add info to output format ('-a help' to see a short list of allowed arguments)\n" " -h no header\n" " -H one header per page\n" "\n" " *Other*\n" #ifdef DEBUG " -d debug output (use as first option)\n" #endif " -V,--version display program version\n" " --help display this message\n" " --usage display short usage message\n" "\n" "NOTE:\n" " without \"selection\" options, owner uid is used as default selection\n" " -C and -p, -A and -u, -l and -o, -h and -H are mutually exclusives\n" " -o pid= implies -h\n" " implies -p\n" " -A add USER info to output\n" #ifdef USE_NEGOPT " -N* add USER,PID,CMD info to output\n" #endif " -N need at least -C or -u\n" #ifdef USE_NEGOPT " - and -N are mutually exclusives\n" " Compose -N* options at your own risk! %%-)\n" #endif "\n" "Try 'man %s' for details.\n" , PROGRAM_NAME , PROGRAM_NAME ); exit( EXIT_SUCCESS ); } /* help */ /********************************************************************** *_____________________________ VERSION _____________________________* **********************************************************************/ /* ** needs explanation? */ #if defined (__STDC__) static void version ( void ) #else static void version ( void ) #endif /* __STDC__ */ { fprintf( stderr , "%s version %s\n" , PROGRAM_NAME , PROGRAM_VERSION ); exit( EXIT_SUCCESS ); } /* version */ /************************************************************************ *_____________________________ PARSE_PID _____________________________* ************************************************************************/ /* Asis from minimal.c */ /* ** set the PID or die */ #if defined (__STDC__) static void parse_pid ( const char * str ) #else static void parse_pid ( str ) const char * str ; #endif /* __STDC__ */ { char * endp; int num; if ( ! str ) goto bad; num = strtol( str , &endp , 0 ); if ( *endp != '\0' ) goto bad; if ( num < 1 ) goto bad; if ( want_one_pid ) goto bad; want_one_pid = (pid_t)num; return; bad: USAGE( "invalid PID '%s'" , str ); } /* parse_pid */ /********************************************************************** *_____________________________ GET_UID _____________________________* **********************************************************************/ /* ** returns the user id from username or die */ #if defined (__STDC__) static fakeconst uid_t get_uid ( const char * username ) #else static fakeconst uid_t get_uid ( username ) const char * username ; #endif /* __STDC__ */ { struct passwd * p; if ( username == NULL ) return -1; p = getpwnam( username ); if ( p ) return p->pw_uid; else { BELL; #if 0 if ( errno == ENOMEM ) fprintf( stderr , "FATAL: Insufficient memory to allocate passwd structure\n" ); else #endif fprintf( stderr , "*** no such user named '%s'\n" , username ); exit( EXIT_FAILURE ); } } /* get_uid */ #ifdef USE_NEGOPT /*************************************************************************** *_____________________________ PARSE_NEGOPT _____________________________* ***************************************************************************/ /* ** parse argument to negate and set the negate mask flag */ #if defined (__STDC__) static void parse_negopt ( const char * ptr ) #else static void parse_negopt ( ptr ) const char * ptr ; #endif /* __STDC__ */ { /* ** -N?? ** ^ (ptr-2) == '-' ** ^ (ptr-1) == 'N' ** ^ (ptr) == 'C' || 'u' ** ^ (ptr+1) == '\0' */ if ( *( ptr + 1 ) ) USAGE( "\"negate\" allows only -N?, not -N?? forms, invalid '-N%s'" , ptr ); /* ** since negopt will be used as next option, we don't need any sanity check here */ switch ( *ptr ) { case 'C': opt_flag.negate |= NEGCMD; return; case 'u': opt_flag.negate |= NEGUSR; return; #ifdef NEGPID case 'p': opt_flag.negate |= NEGPID; return; #endif default: USAGE( "invalid negate selection '%c', only C|u implemented" , *ptr ); } USAGE( "WTF?" ); } /* parse_negopt */ #endif /* USE_NEGOPT */ /************************************************************************** *_____________________________ GET_OPT_ARG _____________________________* **************************************************************************/ /* Adapted from minimal.c */ /* ** Return the next argument, or call the usage function. ** This handles both: -oFOO -o FOO ** (enabled/disabled by return/break in parse_sysv_option()) */ #if defined (__STDC__) static const char * get_opt_arg ( void ) #else static const char * get_opt_arg ( void ) #endif /* __STDC__ */ { const char * ret; ret = flagptr + 1; /* assume argument is part of ps_argv[thisarg] */ DBGprint( "*flag,*ret,ret = [%c] [%c] [%s]\n" , *( ret - 1 ) , *ret , ret ); if ( *ret ) #if 0 /* ** I like option grouping when I do "-lAH", but looks ambiguous in ** cases like "-Cbash -uroot" where "bash" and "root" are arguments ** instead of options... */ return ret; #else /* ** I deny option/argument grouping... */ USAGE( "option and argument CANNOT be grouped [-%s]" , flagptr ); #endif if ( ++thisarg >= ps_argc ) USAGE( "need argument but no one's left" ); /* there is nothing left */ /* argument is the new ps_argv[thisarg] */ ret = ps_argv[thisarg]; if ( ! ret || ! *ret ) USAGE( "invalid argument" ); return ret; } /* get_opt_arg */ /******************************************************************************** *_____________________________ PARSE_SYSV_OPTION _____________________________* ********************************************************************************/ /* Adapted from minimal.c */ /* ** cmdline arguments parsing: 2nd step ** parse SysV options, including Unix98 */ #if defined (__STDC__) static void parse_sysv_option ( void ) #else static void parse_sysv_option ( void ) #endif /* __STDC__ */ { do { switch ( *flagptr ) { #define TWICE( opt ) USAGE( "option '-%c' defined twice" , opt ) #define SET_FLAG( flag ) \ { \ if ( flag ) \ TWICE( *flagptr ); \ flag = TRUE; \ DBGprint( "[debug_opts]: got '%c', flag '%d'\n" , *flagptr , flag ); \ } #ifdef DEBUG case 'd': SET_FLAG( opt_flag.debug ); break; #endif /**** selection ****/ case 'A': SET_FLAG( opt_flag.all ); break; case 'C': SET_FLAG( opt_flag.by_cmd ); if ( want_one_command ) TWICE( *flagptr ); want_one_command = get_opt_arg(); #if 0 return; /* allow -X, I don't like it, looks ambiguous. */ #endif break; case 'p': SET_FLAG( opt_flag.by_pid ); parse_pid( get_opt_arg() ); #if 0 return; /* allow -X, I don't like it, looks ambiguous. */ #endif break; case 'u': SET_FLAG( opt_flag.by_username ); if ( want_one_user ) TWICE( *flagptr ); want_one_user = get_opt_arg(); #if 0 return; /* allow -X, I don't like it, looks ambiguous. */ #endif break; case 'U': SET_FLAG( opt_flag.by_EUID ); break; case 'N': #ifdef USE_NEGOPT if ( *( flagptr + 1 ) ) { const char * negopt = ( flagptr + 1 ); #if 0 /* oldold version, leave as reminder */ /* ** use negopt as N argument: in order to negate C uses "-NC -C command" */ // parse_negopt( ++flagptr ); /* evaluate next char and increments pointer */ #endif /* ** use negopt (e.g. 'C') as next option: in order to negate C uses "-NC command" (not Unix98 compliant but much more elegant) ** negopt will be parsed as next option so sanity check is already implemented (i.e.: -NC and -C options both check on 'C' option ) */ parse_negopt( negopt ); /* evaluate next char but does not increment pointer */ DBGprint( "[debug_opts]: got '%c%c', negate '0x%02X'\n" , *flagptr , *negopt , opt_flag.negate ); } else { if ( opt_flag.negate & NEGALL ) TWICE( *flagptr ); opt_flag.negate |= NEGALL; DBGprint( "[debug_opts]: got '%c', flag '0x%02X'\n" , *flagptr , opt_flag.negate ); } #else SET_FLAG( opt_flag.negate ); #endif break; /**** format ****/ case 'a': SET_FLAG( opt_flag.add ); user_output = get_opt_arg(); /* parsing deferred */ break; case 'h': SET_FLAG( opt_flag.no_header ); break; case 'H': SET_FLAG( opt_flag.page_header ); break; case 'l': SET_FLAG( opt_flag.long_format ); break; case 'o': SET_FLAG( opt_flag.only_pid ); opt_flag.no_header = TRUE; /* We only support a limited form: "-o pid=" (yes, just "pid=") */ if ( strcmp( get_opt_arg() , "pid=" ) ) USAGE( "invalid output format (only 'pid=' allowed)" ); #if 0 return; /* allow -X, I don't like it, looks ambiguous. */ #endif break; /**** other stuff ****/ case 'V': DBGprint( "[debug_opts]: got '%c', version\n" , *flagptr ); version(); break; case '-': /* long options */ if ( *( flagptr + 1 ) ) { const char * arg = ( flagptr + 1 ); DBGprint( "[debug_opts]: got '%c', long option: [%s]\n" , *flagptr , arg ); if ( ! strcmp( arg , "add" ) ) { flagptr += 1; /* move flagptr to 'a' (TWICE can report "-a") */ SET_FLAG( opt_flag.add ); flagptr += 2; /* skip "dd" */ user_output = get_opt_arg(); /* parsing deferred */ } else if ( ! strcmp( arg , "version" ) ) version(); else if ( ! strcmp( arg , "help" ) ) help(); else if ( ! strcmp( arg , "usage" ) ) USAGE( NULL ); else USAGE( "unimplemented long option '--%s'" , arg ); } return; break; case 0: USAGE( "short option lacks" ); default: USAGE( "unimplemented short option '-%c'" , ( isprint( *flagptr ) ? *flagptr : '?' ) ); } /* switch */ } while ( *++flagptr ); #undef TWICE #undef SET_FLAG } /* parse_sysv_option */ /************************************************************************ *_____________________________ ARG_PARSE _____________________________* ************************************************************************/ /* Adapted from minimal.c (funcname and while loop... :) */ /* ** cmdline arguments parsing: 1st step ** arguments parsing and sanity checks */ #if defined (__STDC__) static void arg_parse ( int argc , char * argv[] ) #else static void arg_parse ( argc , argv ) int argc ; char * argv[] ; #endif /* __STDC__ */ { ps_argc = argc; ps_argv = argv; thisarg = 0; /**** iterate over the args ****/ while ( ++thisarg < ps_argc ) { flagptr = ps_argv[thisarg]; switch( *flagptr ) { case '0' ... '9': opt_flag.by_pid = TRUE; parse_pid( flagptr ); break; case '-': flagptr++; parse_sysv_option(); break; default: USAGE( "invalid option '%s' (only UNIX98|GNU style or numeric PID allowed)" , flagptr ); break; } } /**** (paranoid) sanity check and clean-up ****/ DBGprint( "[debug_flags] A C p u U l o a h H __N_\n" ); DBGprint( "[debug_flags] %d %d %d %d %d %d %d %d %d %d 0x%02d\n" , opt_flag.all , opt_flag.by_cmd , opt_flag.by_pid , opt_flag.by_username , opt_flag.by_EUID , opt_flag.long_format , opt_flag.only_pid , opt_flag.add , opt_flag.no_header , opt_flag.page_header , opt_flag.negate ); /* selection flags */ if ( want_one_user ) { want_one_uid = get_uid( want_one_user ); } else if ( ! opt_flag.all && ! want_one_pid && ! want_one_command ) { /* use self uid */ opt_flag.by_username = TRUE; want_one_uid = getuid(); want_one_user = do_user( want_one_uid ); } /********************************** # selection p A u C p 0 ? ? A 0 0 1 u ? 0 1 C ? 1 1 ( A && p ) ( p && A ) ( A && u ) ( u && A ) ( u && p ) ( p && u ) (unimplemented) OK if seeking for user and pid match, conflict otherwise ( C && p ) ( p && C ) (unimplemented) OK if seeking for command and pid match, conflict otherwise **********************************/ /* A && u */ if ( opt_flag.all && opt_flag.by_username ) USAGE( "conflicting selection 'all' and by 'user'" ); /* A && p */ if ( opt_flag.all && opt_flag.by_pid ) USAGE( "conflicting selection 'all' and by 'PID'" ); /* u && p */ if ( opt_flag.by_username && opt_flag.by_pid ) USAGE( "conflicting selection by 'user' and by 'PID'" ); /* u && p (2) */ if ( want_one_user && want_one_pid ) USAGE( "select by 'user' and by 'PID' both requested" ); /* C && p */ if ( opt_flag.by_cmd && opt_flag.by_pid ) USAGE( "command and PID options both specified" ); /* C && p (2) */ if ( want_one_command && want_one_pid ) USAGE( "command and PID arguments both defined" ); /********************************** # format l o h H a l 0 1 1 1 o 0 ! 0 0 h 1 ! 0 1 H 1 0 0 1 a 1 0 1 1 'o' prints ONLY PID(s), conflicts with 'l', 'H' and 'a' ------------------------------------------------------------------- if ( o && ( l || H ) ) usage( "conflicting format: -o && ( -l || -H || -a )" ); ------------------------------------------------------------------- ( o && a ) ( a && o ) ( o && l ) ( l && o ) ( H && o ) ( o && H ) ( H && h ) ( h && H ) **********************************/ /* o && a */ if ( opt_flag.only_pid && opt_flag.add ) USAGE( "conflicting output format -o and -a" ); /* o && l */ if ( opt_flag.only_pid && opt_flag.long_format ) USAGE( "conflicting format -o and -l" ); /* o && H */ if ( opt_flag.only_pid && opt_flag.page_header ) USAGE( "'only PID' and 'one header per page' are both requested" ); /* h && H */ if ( opt_flag.no_header && opt_flag.page_header ) USAGE( "'no header' and 'one header per page' are both requested" ); /********************************** # Negate modifier (implies weird behaviours) -N negate all -NC -Nu negate ( command / user ) p A u C AC uC N 0 0 1 1 1 1 allow: N and COMMAND or USER deny: N and ALL (otherwise, "negate all" means "none") unless C or u defined deny: N and PID (why to exclude one PID?) (combined selections by PID are not implemented) ------------------------------------------------------------------- if ( N && ! C && ! u ) usage( "N require ( -C || -u )" ); ------------------------------------------------------------------- #======================# # NEGATE's TRUTH TABLE # #======================# u : u : 's processes Nu : !u : every process but NOT 's processes, implies -A C : C : every process named , implies -A NC : !C : every process but NOT processes named , implies -A u C : u&C : every process named owned by Nu C : (!u)&C : every process named but NOT owned by , implies -A u NC : u&(!C) : every process owned by but NOT processes named Nu NC : (!u)&(!C) = !(u|C) : every process but NOT processes owned by and NOT processes named , implies -A ------------------------------ u N : !u : NOT 's processes, implies -A (eq. -Nu) Nu N : !(!u) = u : 's processes (eq. -u) C N : !C : every process but NOT processes named , implies -A (eq. -NC) NC N : !(!C) = C : every process named , implies -A (eq. -C) u C N : !(u&C) = (!u)|(!C) : every process but NOT (processes named owned by ), implies -A Nu C N : !((!u)&C) = u|(!C) : every process but NOT processes named unless owned by , implies -A u NC N : !(u&(!C)) = (!u)|C : every process but NOT 's processes unless named , implies -A Nu NC N : !((!u)&(!C)) = u|C : every process owned by or named # take a look at http://www.democritos.it/~baro/sw/ompsinfo/doc/ **********************************/ /* N && ! ( C || u ) */ /* negate what? */ if ( opt_flag.negate && ! ( want_one_command || want_one_user ) ) USAGE( "can negate only username or command" ); /* output format flag */ if ( opt_flag.by_pid ) ps_format = 'p'; if ( opt_flag.by_cmd ) ps_format = 'C'; if ( opt_flag.long_format ) ps_format = 'l'; if ( opt_flag.only_pid ) ps_format = 'o'; DBGprint( "[debug_fmt] ps_format '%c'\n" , ( ps_format ) ? ps_format : '0' ); /* set output format */ __set_output( ps_format ); // define output format if ( ! opt_flag.long_format && ! opt_flag.only_pid ) { /* ** -o: NO ADDS!!! ** -l: NO ADDS!!! ** ** -C: command name isn't really important since command is given as argument ** -p: PID is given as argument ** -u | default: no USER since only one user is selected ** -A: add USER ** -N: add USER CMD */ if ( opt_flag.all ) // add just USER field __show_add( info , E_USER ); if ( opt_flag.negate ) // add USER and CMD fields __show_set( info , negate_fields ); } /* user defined output format */ /* ** always __add_user_ouput() AFTER __set_output() ** user can overwrite any output field */ if ( user_output ) __add_user_output( user_output ); // add, remove, reset/set output format #ifdef USE_OMFS_CHECK /* if requested by user's option, add cwd/oMFS check */ if ( __IS_SET( E_MFS ) ) { omfs.req = TRUE; /* Yes, do read cwd. */ get_omfs_mp(); /* get oMFS mount point */ } #endif #ifdef DEBUG /* show fields */ { info_s * i = info; int el = E_FIRST; while( ++el < E_LAST ) { int s = ( i + el )->show; const char * h = ( i + el )->head; DBGprint( "output field [%2d] is '%d' (%s), [%s]\n" , el , s , (s)?"showed":"hidden" , h ); } } #endif /* DEBUG */ } /* arg_parse */ /************************************************************************ *_____________________________ STAT2PROC _____________________________* ************************************************************************/ /* Almost asis from minimal.c (added RUID handling) */ /* ** return 1 if it works, or 0 for failure */ #if defined (__STDC__) static fakeconst int stat2proc ( const pid_t pid ) #else static fakeconst int stat2proc ( pid ) const pid_t pid ; #endif /* __STDC__ */ { char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */ int num; int fd; char* tmp; struct stat sbuf; /* stat() used to get EUID or RUID */ snprintf( buf , 32 , "/proc/%u/stat" , pid ); if ( ( fd = open( buf , O_RDONLY , 0 ) ) == -1 ) return 0; num = read( fd , buf , sizeof( buf ) - 1 ); if ( opt_flag.by_EUID ) { /* Use effective UID */ fstat( fd , &sbuf ); } else { /* I prefer real UID */ static char dir[32]; snprintf( dir , sizeof dir , "/proc/%u" , pid ); stat( dir , &sbuf ); } P_uid = sbuf.st_uid; close( fd ); if ( num < 80 ) return 0; buf[num] = '\0'; tmp = strrchr( buf , ')' ); /* split into "PID (cmd" and "" */ *tmp = '\0'; /* replace trailing ')' with NUL */ /* parse these two strings separately, skipping the leading "(". */ memset( P_cmd , 0 , sizeof P_cmd ); /* clear */ sscanf( buf , "%d (%15c" , &P_pid , P_cmd ); /* comm[16] in kernel */ num = sscanf( tmp + 2 , /* skip space after ')' too */ "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld " "%lu %lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%u %u %u %u " /* no use for RT signals */ "%lu %lu %lu" , &P_state , &P_ppid , &P_pgrp , &P_session , &P_tty , &P_tpgid , &P_flags , &P_min_flt , &P_cmin_flt , &P_maj_flt , &P_cmaj_flt , &P_utime , &P_stime , &P_cutime , &P_cstime , &P_priority , &P_nice , &P_timeout , &P_it_real_value , &P_start_time , &P_vsize , &P_rss , &P_rss_rlim , &P_start_code , &P_end_code , &P_start_stack , &P_kstk_esp , &P_kstk_eip , &P_signal , &P_blocked , &P_sigignore , &P_sigcatch , &P_wchan , &P_nswap , &P_cnswap ); DBGprint( "stat2proc converted %d fields.\n" , num ); P_vsize /= 1024; P_rss *= ( PAGE_SIZE / 1024 ); if ( num < 30 ) return 0; if ( P_pid != pid ) return 0; return 1; } /* stat2proc */ /********************************************************************** *_____________________________ DO_TIME _____________________________* **********************************************************************/ /* Asis from minimal.c */ #if defined (__STDC__) static const char * do_time ( unsigned long t ) #else static const char * do_time ( t ) unsigned long t ; #endif /* __STDC__ */ { int hh,mm,ss; static char buf[32]; int cnt = 0; t /= HZ; ss = t % 60; t /= 60; mm = t % 60; t /= 60; hh = t % 24; t /= 24; if ( t ) cnt = snprintf( buf , sizeof buf , "%d-" , (int)t ); snprintf( cnt + buf , sizeof( buf ) - cnt , "%02d:%02d:%02d" , hh , mm , ss ); return buf; } /* do_time */ /********************************************************************** *_____________________________ DO_USER _____________________________* **********************************************************************/ /* Almost asis from minimal.c */ #if defined (__STDC__) static const char * do_user ( const uid_t uid ) #else static const char * do_user ( uid ) const uid_t uid ; #endif /* __STDC__ */ { static char buf[32]; static struct passwd * p; static int lastuid = -1; if ( (int)uid != lastuid ) { p = getpwuid( uid ); if ( p ) snprintf( buf , sizeof buf , "%.8s" , p->pw_name ); else snprintf( buf , sizeof buf , "%5d" , uid ); } return buf; } /* do_user */ /********************************************************************** *_____________________________ READINT _____________________________* **********************************************************************/ /* Adapted from openmosix-tools-0.3.4/mps/proc/readproc.c */ /* ** Read integer from /proc// ** e.g.: from oM_files where, nmigs, lock (or any proc file with an integer stored) ** Does not check the validity of the value read from file ('-1' may be a problem). */ #if defined (__STDC__) static fakeconst int readint ( const pid_t pid , const char * file ) #else static fakeconst int readint ( pid , file ) const pid_t pid ; const char * file ; #endif /* __STDC__ */ { static char filename[80]; static char buffer[6]; int fd, num_read, ival; sprintf( filename , "/proc/%u/%s" , pid , file ); DBGprint( "read from file: %s\n" , filename ); if ( ( fd = open( filename , O_RDONLY , 0 ) ) == -1 ) return -1; num_read = read( fd , buffer , sizeof( buffer ) - 1 ); close( fd ); if ( num_read <= 0 ) return -1; ival = atoi( buffer ); return ival; } /* readint */ /*************************************************************************** *_____________________________ READCANTMOVE _____________________________* ***************************************************************************/ /* ** read cantmove string ** set cantflag and return cantmove string. */ #if defined (__STDC__) static const char * readcantmove ( const pid_t pid , char * cantflag ) #else static const char * readcantmove ( pid , cantflag ) const pid_t pid ; char * cantflag ; #endif /* __STDC__ */ { static char filename[80]; int fd, num_read; register char * ptr; // What is the maximum length of "cantmove"? // Assumes the total length of the "char *mosix_stay_string[]" values (fs/proc/array.c), "extern_#" excluded. // monkey, mmap_dev, VM86_mode, daemon, priv_inst, mem_lock, clone_vm, rt_sched, direct_io, init_proc, kiobuf. user_lock, ******** #define CANT_BUF_SIZE 128 static char buffer[CANT_BUF_SIZE]; memset( buffer , 0 , CANT_BUF_SIZE ); sprintf( filename , "/proc/%u/cantmove" , pid ); DBGprint( "read from file: %s\n" , filename ); fd = open( filename , O_RDONLY , 0 ); if ( fd == -1 ) { *cantflag = NOT_AVAIL_FLAG; return NOT_AVAIL; } num_read = read( fd , buffer , CANT_BUF_SIZE - 1 ); close( fd ); if ( num_read > 0 ) { ptr = buffer; while ( ++ptr && *ptr ) if ( *ptr == '\n' || *ptr == '\r' ) *ptr = '\0'; } switch( num_read ) { case 0: *cantflag = MIGRATABLE_FLAG; return MIGRATABLE; case -1: *cantflag = NOT_AVAIL_FLAG; return NOT_AVAIL; default: *cantflag = CANTMOVE_FLAG; return buffer; } return "WTF?"; #undef CANT_BUF_SIZE } /* readcantmove */ /****************************************************************************** *_____________________________ GET_SCREEN_ROWS _____________________________* ******************************************************************************/ /* Adapted from procps-3.1.13/ps/global.c ('column' removed) */ /* ** The rules: ** 1. Defaults are implementation-specific. (ioctl,termcap,guess) ** 2. LINES overrides the default. (standards compliance) */ #if defined (__STDC__) static fakeconst int get_screen_rows ( void ) #else static fakeconst int get_screen_rows ( void ) #endif /* __STDC__ */ { struct winsize ws; char * lines; /* Unix98 environment variable */ int r = -1; /* ** If I explicitly ask "one header per page" (-H), can my page has more ** than 256 lines ??? (with 'xterm -fn 5x7' @ 1280x960 I reach 123 lines) */ #define MAX_LINES_WITHOUT_HEADER 256 if ( ioctl( 1 , TIOCGWINSZ , &ws ) != -1 && ws.ws_row > 0 ) r = ws.ws_row; lines = getenv( "LINES" ); if ( lines && *lines ) { long t; char * endptr; t = strtol( lines , &endptr , 0 ); if ( ! *endptr && ( t > 0 ) && ( t < (long)MAX_LINES_WITHOUT_HEADER ) ) r = (int)t; } if ( r < 2 ) r = 24; /* standard 80x24 */ DBGprint( "LINES = %d\n" , r - 1 ); return r - 1; /* reserve one line for the header */ #undef MAX_LINES_WITHOUT_HEADER } /* get_screen_rows */ #ifndef USE_NEGOPT /********************************************************************* *_____________________________ NEGATE _____________________________* *********************************************************************/ /* ** do negate (old version) */ #if defined (__STDC__) static fakeconst int negate ( const int condition ) #else static fakeconst int negate ( condition ) const int condition ; #endif /* __STDC__ */ { return ( opt_flag.negate ) ? ( ! condition ) : ( condition ); } /* negate */ #else /********************************************************************* *_____________________________ NEGATE _____________________________* *********************************************************************/ /* ** do negate (new version) ** if the flag match the user's option, do negate */ #if defined (__STDC__) static fakeconst int negate ( int condition , const int flag ) #else static fakeconst int negate ( condition , flag ) const int condition ; const int flag ; #endif /* __STDC__ */ { DBGprint( "condition,negate,flag: %d,0x%02X,0x%02X\n" , condition , opt_flag.negate , flag ); return ( ( opt_flag.negate & flag ) == flag ) ? ( ! condition ) : ( condition ); /* clearest than !( ( opt_flag.negate & flag ) ^ flag ) */ } /* negate */ #endif /*************************************************************************** *_____________________________ MSX_IS_MOSIX _____________________________* ***************************************************************************/ /* Asis from openmosix-tools-0.3.4/moslib/libmosix.c */ /* ** check if it is an openMosix enabled kernel (/proc/hpc is created and ** used by oM) */ #if defined (__STDC__) static fakeconst int msx_is_mosix ( void ) #else static fakeconst int msx_is_mosix ( void ) #endif /* __STDC__ */ { return ( ! access( "/proc/hpc/" , 0 ) ); } /* msx_is_mosix */ /************ * MAIN * ************/ #if defined (__STDC__) int main ( int argc , char * argv[] ) #else int main ( argc , argv ) int argc ; char * argv[] ; #endif /* __STDC__ */ { #ifdef DEBUG (void)setvbuf( stdout , (char *)NULL , _IONBF , 0 ); (void)setvbuf( stderr , (char *)NULL , _IONBF , 0 ); #else (void)setvbuf( stdout , (char *)NULL , _IOLBF , 0 ); (void)setvbuf( stderr , (char *)NULL , _IOLBF , 0 ); #endif #ifdef USE_CMDLINE dummy(); printf( "[%s]\n" , dest ); exit( 1 ); #endif /* before anything, warn about !oM */ if ( ! msx_is_mosix() ) { BELL; fprintf( stderr , "*** Cannot find the /proc/hpc directory.\n" ); fprintf( stderr , "*** Looks like this is not an openMosix enabled kernel.\n" ); } arg_parse( argc , argv ); __print_header( info ); if ( want_one_pid ) { #ifdef USE_NEGATE_PID #error "Negate PID is not implemented (yet?)" if ( p && u && C ) "OK: PID of command is owned by user" if ( p && u && ! C ) "FAIL: PID owned by user but not of command" if ( p && ! u && C ) "FAIL: PID of command is not owned by user" if ( p && ! u && ! C ) "FAIL: PID not owned by user and not of command" #endif if ( stat2proc( want_one_pid ) ) __set_procinfo(), __print_procinfo( info ); else { BELL; fprintf( stderr , "*** no such process with PID '%u'\n" , want_one_pid ); exit( EXIT_FAILURE ); } } else { struct direct * ent; /* dirent handle */ DIR * dir; int found_a_proc; int screen_rows; screen_rows = get_screen_rows(); found_a_proc = 0; dir = opendir( "/proc" ); if ( dir == NULL ) { BELL; fprintf( stderr , "Hey! Cannot open /proc! [%d] %m\n" , errno ); exit( EXIT_FAILURE ); } while ( ( ent = readdir( dir ) ) ) { if ( *ent->d_name < '0' || *ent->d_name > '9' ) continue; if ( ! stat2proc( atoi( ent->d_name ) ) ) continue; if ( want_one_user && want_one_command ) { #ifdef USE_NEGOPT if ( _NEGATE( _NEGOPT( ( want_one_uid != P_uid ) , NEGUSR ) || _NEGOPT( strcmp( want_one_command , P_cmd ) , NEGCMD ) ) ) #else if ( _NEGATE( ( want_one_uid != P_uid ) || strcmp( want_one_command , P_cmd ) ) ) #endif continue; } else { /* ** If only one defined act as usually. */ if ( want_one_user ) { DBGprint( "USER: wuid,Puid,cond: %d, %d, %d" "\t%s|%s\n" , want_one_uid , P_uid , _NEGOPT( ( want_one_uid != P_uid ) , NEGUSR ) , P_cmd, do_user( P_uid ) ); #ifdef USE_NEGOPT if ( _NEGATE( _NEGOPT( ( want_one_uid != P_uid ) , NEGUSR ) ) ) #else if ( _NEGATE( want_one_uid != P_uid ) ) #endif continue; } if ( want_one_command ) { DBGprint( "CMD: wcmd,Pcmd,cond: %s, %s, %d" "\t%s|%s\n" , want_one_command , P_cmd , _NEGOPT( ( want_one_uid != P_uid ) , NEGCMD ) , P_cmd, do_user( P_uid ) ); #ifdef USE_NEGOPT if ( _NEGATE( _NEGOPT( strcmp( want_one_command , P_cmd ) , NEGCMD ) ) ) #else if ( _NEGATE( strcmp( want_one_command , P_cmd ) ) ) #endif continue; } } /* skip first */ if ( opt_flag.page_header && found_a_proc ) { if ( ! ( found_a_proc % screen_rows ) ) __print_header( info ); } ++found_a_proc; __set_procinfo(), __print_procinfo( info ); } closedir( dir ); if ( ! found_a_proc ) { /* no process match the selection */ BELL; if ( ! opt_flag.negate ) { if ( opt_flag.by_username && opt_flag.by_cmd ) { fprintf( stderr , "*** no such process named '%s' owned by '%s'\n" , want_one_command , want_one_user ); } else { if ( want_one_user ) fprintf( stderr , "*** no such process owned by '%s'\n" , want_one_user ); if ( want_one_command ) fprintf( stderr , "*** no such process named '%s'\n" , want_one_command ); } } else { //#warning "FIXME: no match? test: -NC pippo -N" /* a user's negated selection is hard to parse here, simply notify "no match" */ fprintf( stderr , "*** negate selection: no such process\n" ); } } /* no proc */ exit( ! found_a_proc ); } return EXIT_SUCCESS; /* here if PID found */ } /* main */ /*********************** E N D O F F I L E ************************/