projekt

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

main.c (15411B)


      1 #include <errno.h>
      2 #include <getopt.h>
      3 #include <signal.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <sys/wait.h>
      8 #include <sys/utsname.h>
      9 #include <time.h>
     10 #include <unistd.h>
     11 #include <bpf/libbpf.h>
     12 #include "process_exit.h"
     13 #include "process_exit.skel.h"
     14 
     15 #define PCOMM_FLAG    0x00001
     16 #define TGID_FLAG     0x00002
     17 #define PID_FLAG      0x00004
     18 #define PPID_FLAG     0x00008
     19 #define UID_FLAG      0x00010
     20 #define AGE_FLAG      0x00020
     21 #define UTIME_FLAG    0x00040
     22 #define STIME_FLAG    0x00080
     23 #define EXIT_FLAG     0x00100
     24 #define EXITSIG_FLAG  0x00200
     25 #define NVCS_FLAG     0x00400
     26 #define NIVCS_FLAG    0x00800
     27 #define CUTIME_FLAG   0x01000
     28 #define CSTIME_FLAG   0x02000
     29 #define INBLOCK_FLAG  0x04000
     30 #define OUBLOCK_FLAG  0x08000
     31 #define CINBLOCK_FLAG 0x10000
     32 #define COUBLOCK_FLAG 0x20000
     33 #define TIME_FLAG     0x40000
     34 
     35 static int libbpf_print_fn(enum libbpf_print_level level,
     36                            const char* format, va_list args) {
     37     if (level >= LIBBPF_DEBUG)
     38         return 0;
     39 
     40     return vfprintf(stderr, format, args);
     41 }
     42 
     43 static int csv = 0;
     44 static FILE* output_file = 0;
     45 static uint32_t parameter_flags = 0x7ffff; // defaultne vsetky parametre
     46 static int enabled_flag_count = 0;
     47 static int current_flags_printed = 1;
     48 static uint64_t processes_handled = 0;
     49 static int cumulative = 0;
     50 static volatile __sig_atomic_t running = 1;
     51 static uint32_t filtered_uid = 0;
     52 
     53 static void parse_filter_flags(char* flag_string) {
     54     const char* token = strtok(flag_string, ",");
     55     while (token) {
     56         if      (strcmp(token, "pcomm")    == 0) parameter_flags |= PCOMM_FLAG;
     57         else if (strcmp(token, "tgid")     == 0) parameter_flags |= TGID_FLAG;
     58         else if (strcmp(token, "pid")      == 0) parameter_flags |= PID_FLAG;
     59         else if (strcmp(token, "ppid")     == 0) parameter_flags |= PPID_FLAG;
     60         else if (strcmp(token, "uid")      == 0) parameter_flags |= UID_FLAG;
     61         else if (strcmp(token, "age")      == 0) parameter_flags |= AGE_FLAG;
     62         else if (strcmp(token, "utime")    == 0) parameter_flags |= UTIME_FLAG;
     63         else if (strcmp(token, "stime")    == 0) parameter_flags |= STIME_FLAG;
     64         else if (strcmp(token, "exit")     == 0) parameter_flags |= EXIT_FLAG;
     65         else if (strcmp(token, "exitsig")  == 0) parameter_flags |= EXITSIG_FLAG;
     66         else if (strcmp(token, "nvcs")     == 0) parameter_flags |= NVCS_FLAG;
     67         else if (strcmp(token, "nivcs")    == 0) parameter_flags |= NIVCS_FLAG;
     68         else if (strcmp(token, "cutime")   == 0) parameter_flags |= CUTIME_FLAG;
     69         else if (strcmp(token, "cstime")   == 0) parameter_flags |= CSTIME_FLAG;
     70         else if (strcmp(token, "inblock")  == 0) parameter_flags |= INBLOCK_FLAG;
     71         else if (strcmp(token, "oublock")  == 0) parameter_flags |= OUBLOCK_FLAG;
     72         else if (strcmp(token, "cinblock") == 0) parameter_flags |= CINBLOCK_FLAG;
     73         else if (strcmp(token, "coublock") == 0) parameter_flags |= COUBLOCK_FLAG;
     74         else                                     fprintf(stderr, "Unknown parameter '%s'\n", token);
     75 
     76         token = strtok(NULL, ",");
     77     }
     78 }
     79 
     80 static void print_help(char* argv[]) {
     81     fprintf(stdout,
     82             "Usage: %s [OPTION]...\n"
     83             "Print information about terminated processes.\n"
     84             "\n"
     85             "Options:\n"
     86             "  -c, --csv                          use csv format\n"
     87             "  -f FILE, --file FILE               print to file FILE\n"
     88             "  -F params..., --filter params...   filter process information based on\n"
     89             "                                     params\n"
     90             "  -u UID, --uid UID                  show only processes owened by user\n"
     91             "                                     with uid UID\n"
     92             "  -C, --cumulative                   show cumulative data for utime,\n"
     93             "                                     stime, inblock, oublock, filters\n"
     94             "                                     out cutime, cstime, cinblock,\n"
     95             "                                     coublock\n"
     96             "  -h, --help                         print this help message and exit\n"
     97             "\n"
     98             "Filter parameters:\n"
     99             "  pcomm, tgid, pid, ppid, uid, age, utime, stime, exit, exitsig, nvcs,\n"
    100             "  nivcs, cutime, cstime, inblock, oublock, cinblock, coublock\n",
    101             argv[0]);
    102 }
    103 
    104 static void parse_options(int argc, char* argv[], const char** file) {
    105     static struct option long_options[] = {
    106         { "csv"        ,       no_argument, 0, 'c' },
    107         { "file"       , required_argument, 0, 'f' },
    108         { "filter"     , required_argument, 0, 'F' },
    109         { "cumulative" ,       no_argument, 0, 'C' },
    110         { "help"       ,       no_argument, 0, 'h' },
    111         { "uid"        , required_argument, 0, 'u' },
    112         { 0            ,                 0, 0,   0 },
    113     };
    114 
    115     int ret = EXIT_FAILURE;
    116     int opt;
    117     while ((opt = getopt_long(argc, argv, "cf:hF:Cu:", long_options, NULL)) != -1) {
    118         switch (opt) {
    119         case 'c':
    120             csv = 1;
    121             break;
    122         case 'f':
    123             *file = optarg;
    124             break;
    125         case 'F':
    126             parameter_flags = 0;
    127             parameter_flags |= TIME_FLAG; // zatial defaultne nechame TIME_FLAG
    128             parse_filter_flags(optarg);
    129             break;
    130         case 'C':
    131             cumulative = 1;
    132             break;
    133         case 'u':
    134             errno = 0;
    135             filtered_uid = strtoul(optarg, NULL, 10);
    136             if (errno != 0 || optarg[0] == '-') {
    137                 fprintf(stderr, "Invalid UID after -u.\n");
    138                 exit(EXIT_FAILURE);
    139             }
    140             break;
    141         case 'h':
    142             ret = EXIT_SUCCESS;
    143         default:
    144             print_help(argv);
    145             exit(ret);
    146         }
    147     }
    148 }
    149 
    150 static void print_int_parameter2(__u64 parameter, const char* format) {
    151     if (csv) {
    152         format = "%d";
    153         if (current_flags_printed < enabled_flag_count) {
    154             format = "%d,";
    155             current_flags_printed++;
    156         } else {
    157             current_flags_printed = 1;
    158         }
    159     }
    160     fprintf(output_file, format, parameter);
    161 }
    162 
    163 static void print_int_parameter(__u64 parameter) {
    164     print_int_parameter2(parameter, "%-7d\t");
    165 }
    166 
    167 static void print_double_parameter(double parameter) {
    168     const char* format = "%-7.3f\t";
    169     if (csv) {
    170         format = "%.3f";
    171         if (current_flags_printed < enabled_flag_count) {
    172             format = "%.3f,";
    173             current_flags_printed++;
    174         } else {
    175             current_flags_printed = 1;
    176         }
    177     }
    178     fprintf(output_file, format, parameter);
    179 }
    180 
    181 static void print_pcomm_parameter(const char* parameter) {
    182     const char* format = "%-16s";
    183     if (csv) {
    184         format = "%s";
    185         if (current_flags_printed < enabled_flag_count) {
    186             format = "%s,";
    187             current_flags_printed++;
    188         } else {
    189             current_flags_printed = 1;
    190         }
    191     }
    192     fprintf(output_file, format, parameter);
    193 }
    194 
    195 static void print_string_parameter(const char* parameter) {
    196     const char* format = "%s\t";
    197     if (csv) {
    198         format = "%s";
    199         if (current_flags_printed < enabled_flag_count) {
    200             format = "%s,";
    201             current_flags_printed++;
    202         } else {
    203             current_flags_printed = 1;
    204         }
    205     }
    206     fprintf(output_file, format, parameter);
    207 }
    208 
    209 static void print_time_parameter(time_t sec, time_t ns) {
    210     const char* format = "%d.%06d\t";
    211     if (csv) {
    212         format = "%d.%06d";
    213         if (current_flags_printed < enabled_flag_count) {
    214             format = "%d.%06d,";
    215             current_flags_printed++;
    216         } else {
    217             current_flags_printed = 1;
    218         }
    219     }
    220     fprintf(output_file, format, sec, ns / 1000);
    221 }
    222 
    223 static void handle_event(void* ctx, int cpu, void* data, unsigned int data_sz) {
    224     struct data_t* event = (struct data_t*)data;
    225     (void)ctx;
    226     (void)cpu;
    227     (void)data_sz;
    228 
    229     if (filtered_uid != 0 && filtered_uid != event->uid) {
    230         return;
    231     }
    232 
    233     if (cumulative) {
    234         event->utime += event->cutime;
    235         event->stime += event->cstime;
    236         event->inblock += event->cinblock;
    237         event->oublock += event->coublock;
    238     }
    239 
    240     processes_handled++;
    241 
    242     struct timespec tm;
    243     if (clock_gettime(CLOCK_REALTIME, &tm) == -1) {
    244         perror("clock_gettime");
    245         exit(EXIT_FAILURE);
    246     }
    247 
    248     if (parameter_flags & TIME_FLAG    ) print_time_parameter(tm.tv_sec, tm.tv_nsec);
    249     if (parameter_flags & PCOMM_FLAG   ) print_pcomm_parameter(event->task);
    250     if (parameter_flags & TGID_FLAG    ) print_int_parameter(event->tgid);
    251     if (parameter_flags & PID_FLAG     ) print_int_parameter(event->pid);
    252     if (parameter_flags & PPID_FLAG    ) print_int_parameter(event->ppid);
    253     if (parameter_flags & UID_FLAG     ) print_int_parameter(event->uid);
    254     if (parameter_flags & AGE_FLAG     ) print_double_parameter((event->exit_time - event->start_time) / 1e9);
    255     if (parameter_flags & UTIME_FLAG   ) print_double_parameter(event->utime / 1e9);
    256     if (parameter_flags & STIME_FLAG   ) print_double_parameter(event->stime / 1e9);
    257     if (parameter_flags & EXIT_FLAG    ) print_int_parameter(WIFEXITED(event->exit_code) ? WEXITSTATUS(event->exit_code) : -1);
    258     if (parameter_flags & EXITSIG_FLAG ) print_int_parameter(WIFSIGNALED(event->exit_code) ? WTERMSIG(event->exit_code) : -1);
    259     if (parameter_flags & NVCS_FLAG    ) print_int_parameter(event->nvcsw);
    260     if (parameter_flags & NIVCS_FLAG   ) print_int_parameter(event->nivcsw);
    261     if (parameter_flags & CUTIME_FLAG  ) print_double_parameter(event->cutime / 1e9);
    262     if (parameter_flags & CSTIME_FLAG  ) print_double_parameter(event->cstime / 1e9);
    263     if (parameter_flags & INBLOCK_FLAG ) print_int_parameter(event->inblock);
    264     if (parameter_flags & OUBLOCK_FLAG ) print_int_parameter(event->oublock);
    265     if (parameter_flags & CINBLOCK_FLAG) print_int_parameter(event->cinblock);
    266     if (parameter_flags & COUBLOCK_FLAG) print_int_parameter(event->coublock);
    267     fprintf(output_file, "\n");
    268 
    269     fflush(output_file);
    270 }
    271 
    272 static void lost_event(void* ctx, int cpu, long long unsigned cnt) {
    273     (void)ctx;
    274     (void)cpu;
    275     (void)cnt;
    276 
    277     printf("lost event\n");
    278 }
    279 
    280 static void print_header(void) {
    281     time_t start_time = time(NULL);
    282     fprintf(output_file, "start timestamp: %s", ctime(&start_time));
    283 
    284     struct utsname sysinfo;
    285     if (uname(&sysinfo) == -1) {
    286         perror("uname");
    287         exit(EXIT_FAILURE);
    288     }
    289 
    290     fprintf(output_file,
    291             "system info: %s %s %s %s %s\n",
    292             sysinfo.sysname,
    293             sysinfo.nodename,
    294             sysinfo.release,
    295             sysinfo.version,
    296             sysinfo.machine);
    297 
    298     if (parameter_flags & TIME_FLAG    ) print_string_parameter("TIME");
    299     if (parameter_flags & PCOMM_FLAG   ) print_pcomm_parameter("PCOMM");
    300     if (parameter_flags & TGID_FLAG    ) print_string_parameter("TGID");
    301     if (parameter_flags & PID_FLAG     ) print_string_parameter("PID");
    302     if (parameter_flags & PPID_FLAG    ) print_string_parameter("PPID");
    303     if (parameter_flags & UID_FLAG     ) print_string_parameter("UID");
    304     if (parameter_flags & AGE_FLAG     ) print_string_parameter("age");
    305     if (parameter_flags & UTIME_FLAG   ) print_string_parameter("utime");
    306     if (parameter_flags & STIME_FLAG   ) print_string_parameter("stime");
    307     if (parameter_flags & EXIT_FLAG    ) print_string_parameter("exit");
    308     if (parameter_flags & EXITSIG_FLAG ) print_string_parameter("exitsig");
    309     if (parameter_flags & NVCS_FLAG    ) print_string_parameter("nvcsw");
    310     if (parameter_flags & NIVCS_FLAG   ) print_string_parameter("nivcsw");
    311     if (parameter_flags & CUTIME_FLAG  ) print_string_parameter("cutime");
    312     if (parameter_flags & CSTIME_FLAG  ) print_string_parameter("cstime");
    313     if (parameter_flags & INBLOCK_FLAG ) print_string_parameter("inblk");
    314     if (parameter_flags & OUBLOCK_FLAG ) print_string_parameter("oublk");
    315     if (parameter_flags & CINBLOCK_FLAG) print_string_parameter("cinblk");
    316     if (parameter_flags & COUBLOCK_FLAG) print_string_parameter("coublk");
    317     fprintf(output_file, "\n");
    318 
    319     fflush(output_file);
    320 }
    321 
    322 static void catch_interrupt(int signal) {
    323     (void)signal;
    324     running = 0;
    325 }
    326 
    327 int main(int argc, char* argv[]) {
    328 
    329     output_file = stdout;
    330     const char* file_name = NULL;
    331     parse_options(argc, argv, &file_name);
    332     if ((enabled_flag_count = __builtin_popcount(parameter_flags)) == 0) {
    333         fprintf(stderr, "No valid parameters in filter\n");
    334         exit(EXIT_FAILURE);
    335     }
    336 
    337     if (cumulative) {
    338         // pri kumulativnom vypise nahradime hodnoty suctom
    339         // rodicovskych a dcerskych hodnot, preto nebudeme vypisovat
    340         // cutime, cstime, atd.
    341         parameter_flags &= ~CUTIME_FLAG;
    342         parameter_flags &= ~CSTIME_FLAG;
    343         parameter_flags &= ~CINBLOCK_FLAG;
    344         parameter_flags &= ~COUBLOCK_FLAG;
    345         enabled_flag_count -= 4;
    346     }
    347 
    348     if (getuid() != 0) {
    349         fprintf(stderr, "You should run this program with root privileges (sudo).\n");
    350         exit(EXIT_FAILURE);
    351     }
    352 
    353     libbpf_set_print(libbpf_print_fn);
    354 
    355     struct process_exit_bpf* skel = process_exit_bpf__open_and_load();
    356     if (!skel) {
    357         fprintf(stderr, "Failed to open BPF object file\n");
    358         return 1;
    359     }
    360 
    361     int err = process_exit_bpf__attach(skel);
    362     if (err) {
    363         fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
    364         process_exit_bpf__destroy(skel);
    365         return 1;
    366     }
    367 
    368 #ifndef OLD_LIBBPF
    369     struct perf_buffer* pb = perf_buffer__new(bpf_map__fd(skel->maps.output),
    370                                               8,
    371                                               handle_event,
    372                                               lost_event,
    373                                               NULL,
    374                                               NULL);
    375 #else
    376     const struct perf_buffer_opts opts = {
    377         handle_event,
    378         lost_event,
    379         NULL
    380     };
    381     struct perf_buffer* pb = perf_buffer__new(bpf_map__fd(skel->maps.output),
    382                                               8,
    383                                               &opts);
    384 #endif
    385     if (!pb) {
    386         err = -1;
    387         fprintf(stderr, "Failed to create ring buffer\n");
    388         process_exit_bpf__destroy(skel);
    389         return -err;
    390     }
    391 
    392     if (signal(SIGINT, catch_interrupt) == SIG_ERR || signal(SIGTERM, catch_interrupt) == SIG_ERR) {
    393         fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno));
    394         err = 1;
    395         running = 0;
    396     }
    397 
    398     int writing_to_file = 0;
    399     if (file_name != NULL && strcmp(file_name, "-") != 0) {
    400         output_file = fopen(file_name, "w+");
    401         writing_to_file = 1;
    402     }
    403 
    404     print_header();
    405     while (running) {
    406         err = perf_buffer__poll(pb, 1000);
    407         if (err == -EINTR) {
    408             err = 0;
    409             break;
    410         }
    411         if (err < 0) {
    412             printf("Error polling perf buffer: %d\n", err);
    413             break;
    414         }
    415     }
    416 
    417     printf("\nHandled %lu processes.\n", processes_handled);
    418     time_t end_time = time(NULL);
    419     fprintf(output_file, "end timestamp: %s", ctime(&end_time));
    420     fflush(output_file);
    421 
    422     if (writing_to_file) {
    423         fclose(output_file);
    424     }
    425 
    426     perf_buffer__free(pb);
    427     process_exit_bpf__destroy(skel);
    428 
    429     return -err;
    430 }