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 }