/* $Id: listps.c 15 2007-02-08 14:03:09Z csl $
 *
 * listps -- lists all running processes on linux (/proc)
 *
 * Copyright (C) 2004, 2007 Christian Stigen Larsen <csl@sublevel3.org>
 * Distributed under the GNU General Public License (GPL) v2 or later.
 *
 * Compile with e.g.:    gcc -s -O3 -olistps listps.cpp
 *
 * This program is based on scripts found on the web while searching
 * for information on the suckit rootkit.
 *
 * For more information, see http://csl.sublevel3.org/listps/
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

typedef struct options_t {
	int pid_start;
	int pid_stop;
	int show_all;
} options_t;

options_t def_opts = {0, 33000, 1};

void version() {
	fputs(
	"listps 0.9.0 2007-02-08\n"
	"Copyright (C) 2004, 2007 Christian Stigen Larsen <csl@sublevel3.org>\n"
	"Distributed under the GNU General Public License (GPL) v2 or later.\n"
	"\n", stderr);
}

void help() {
	fputs(
	"Usage: listps [ option(s) ]\n"
	"\n"
	"Will try to list all running processes (including hidden ones) by\n"
	"expliticly checking the /proc/ filesystem.\n"
	"\n"
	"Swapped-out processes are printed in paranthesis.\n"
	"\n"
	"This utility works very well with some rootkits, e.g.\n"
        "suckit 1.3e, which inserts a linux kernel module to perform\n"
	"process hiding.\n"
	"\n"
    	"Options:\n"
        "  -bNUMBER  first PID to check (default is 0)\n"
        "  -d        only print hidden processes\n"
        "  -eNUMBER  last PID to check (default is 33000)\n"
        "  -h        print help\n"
        "  -v        print version\n"
	"\n", stderr);
}

void parse_args(char* argv[]) {
    while ( *++argv ) {
        if ( **argv == '-' )
            switch ( (*argv)[1] ) {
            default:
                printf("Unknown option -%c\n\n", (*argv)[1]);
                version(); help(); exit(1);
                break;
            case 'h':
                version(); help(); exit(0);
                break;
            case 'v':
                version(); exit(0);
                break;
            case 'd':
                def_opts.show_all = 0;
                break;
            case 'b':
                if ( (def_opts.pid_start = atoi((*argv)+2)) <= 0 ) {
                    fprintf(stderr, "error: invalid number, use format -bNUMBER, e.g. -b10\n");
                    exit(1);
                }
		break;
            case 'e':
                if ( (def_opts.pid_stop = atoi((*argv)+2)) <= 0 ) {
                    fprintf(stderr, "error: invalid number, use format -eNUMBER, e.g. -e10\n");
                    exit(1);
                }
		break;
            }
    }

    if ( def_opts.pid_stop<def_opts.pid_start || def_opts.pid_start<0 || def_opts.pid_stop<0 ) {
        fprintf(stderr, "error: invalid PID-range\n");
        exit(1);
    }
}

char* mk_filename(const int pid, const char* file) {
    static char s[64];
    if ( !file )
        sprintf(s, "/proc/%d", pid);
    else
        sprintf(s, "/proc/%d/%s", pid, file);
    return s;
}

char* getline(FILE *f) {
    static char s[1024];
    *s = 0;
    if ( f ) fgets(s, sizeof(s), f);
    return s;
}

int is_hidden_pid(const int pid) {
    return chdir(mk_filename(pid, 0)) != 0;
}

char* skipto(char* s, char stop) {
    while ( *s && *s!=stop ) ++s;
    return s;
}

void listpids() {
    FILE *f, *fs;
    int pid;
    char *p;

    printf("Checking pids from %d to %d\n", def_opts.pid_start, def_opts.pid_stop);

    if ( def_opts.show_all ) printf("  PID COMMAND\n");

    for ( pid = def_opts.pid_start; pid < def_opts.pid_stop; ++pid ) {
        if ( (f = fopen(mk_filename(pid, "cmdline"), "rb"))!=NULL ) {
            p = getline(f);
            fclose(f);

            if ( !*p ) {
               if ( (fs = fopen(mk_filename(pid, "stat"), "rb"))!=NULL ) {
                    p = skipto(getline(fs), ' ')+1; // skip first field
                    *skipto(p, ' ') = 0; // terminate here
                    fclose(fs);
                }
            }

            int hidden = is_hidden_pid(pid);

            if ( def_opts.show_all || hidden )
                printf("%5d %s%s\n", pid, p, hidden? " (hidden)" : "");
        }
    }
}

int main(int argc, char** argv) {
    parse_args(argv);
    listpids();
    return 0;
}
