forked from draios/sysdig
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsysdig.cpp
More file actions
2048 lines (1879 loc) · 60.8 KB
/
sysdig.cpp
File metadata and controls
2048 lines (1879 loc) · 60.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright (C) 2013-2020 Sysdig Inc.
This file is part of sysdig.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#define __STDC_FORMAT_MACROS
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <assert.h>
#include <algorithm>
#include <atomic>
#include <libsinsp/sinsp.h>
#include <libsinsp/sinsp_cycledumper.h>
#include <libsinsp/scap_open_exception.h>
#include <libsinsp/utils.h>
#include <libsinsp/plugin.h>
#include <libsinsp/plugin_manager.h>
#include <chisel/chisel_capture_interrupt_exception.h>
#ifdef HAS_CAPTURE
#ifndef WIN32
#include "driver_config.h"
#endif // WIN32
#endif // HAS_CAPTURE
#include "sysdig.h"
#ifdef HAS_CHISELS
#include <chisel/chisel.h>
#include <chisel/chisel_utils.h>
#include <chisel/chisel_fields_info.h>
#endif
#include "filterchecks/sinsp_filtercheck_syslog.h"
#include "utils/sinsp_opener.h"
#include "utils/plugin_utils.h"
#include "utils/supported_events.h"
#include "utils/supported_fields.h"
#ifdef _WIN32
#include "win32/getopt.h"
#include <io.h>
#else
#include <unistd.h>
#include <getopt.h>
#include <termios.h>
#endif
static constexpr const char* g_unknown_field_err = "nonexistent field ";
static std::atomic<bool> g_terminate = false;
static std::atomic<bool> g_terminating = false;
static std::atomic<bool> g_plugin_input = false;
#ifdef HAS_CHISELS
std::vector<sinsp_chisel*> g_chisels;
#endif
enum color_term_out {
COLOR = 0,
NO_COLOR,
FORCE_COLOR
};
static void usage();
//
// Helper functions
//
static void signal_callback(int signal)
{
if(g_plugin_input)
{
//
// Input plugins can get stuck in next().
// When we are using one, check again in few seconds and force a quit
// if we are stuck.
//
if(g_terminate == true && g_terminating == false)
{
exit(0);
}
else
{
g_terminate = true;
#ifndef _WIN32
alarm(3);
#endif
}
}
else
{
g_terminate = true;
}
}
//
// Program help
//
static void usage()
{
printf(
"sysdig version " SYSDIG_VERSION "\n"
"Usage: sysdig [options] [-p <output_format>] [filter]\n\n"
"Options:\n"
" -A, --print-ascii Only print the text portion of data buffers, and echo\n"
" end-of-lines. This is useful to only display human-readable\n"
" data.\n"
" -b, --print-base64 Print data buffers in base64. This is useful for encoding\n"
" binary data that needs to be used over media designed to\n"
" handle textual data (i.e., terminal or json).\n"
" -B<bpf_probe>, --bpf=<bpf_probe>\n"
" Enable live capture using the specified BPF probe instead of the kernel module.\n"
" The BPF probe can also be specified via the environment variable\n"
" SYSDIG_BPF_PROBE. If <bpf_probe> is left empty, sysdig will\n"
" try to load one from the scap-driver-loader script.\n"
#ifdef HAS_CHISELS
" -c <chiselname> <chiselargs>, --chisel <chiselname> <chiselargs>\n"
" run the specified chisel. If the chisel require arguments,\n"
" they must be specified in the command line after the name.\n"
" -cl, --list-chisels\n"
" lists the available chisels. Sysdig looks for chisels in the\n"
" following directories: ./chisels, ~/.chisels, " SYSDIG_CHISELS_DIR ".\n"
#endif
#ifdef HAS_MODERN_BPF
" --cpus-for-each-buffer <cpus_num>\n"
" Please note this config regards only the modern BPF probe.\n"
" How many CPUs you want to assign to a single syscall buffer (ring buffer).\n"
" By default, every syscall buffer is associated to 2 CPUs, so the mapping is\n"
" 1:2. The modern BPF probe allows you to choose different mappings, for\n"
" example, 1:1 would mean a syscall buffer for each CPU.\n"
#endif
" -C <file_size>, --file-size=<file_size>\n"
" Before writing an event, check whether the file is\n"
" currently larger than file_size and, if so, close the\n"
" current file and open a new one. Saved files will have the\n"
" name specified with the -w flag, with a number after it,\n"
" starting at 0 and continuing upward. The units of file_size\n"
" are millions of bytes (10^6, not 2^20). Use the -W flag to\n"
" determine how many files will be saved to disk.\n"
" -d, --displayflt Make the given filter a display one\n"
" Setting this option causes the events to be filtered\n"
" after being parsed by the state system. Events are\n"
" normally filtered before being analyzed, which is more\n"
" efficient, but can cause state (e.g. FD names) to be lost.\n"
" -D, --debug Capture events about sysdig itself, display internal events\n"
" in addition to system events, and print additional\n"
" logging on standard error.\n"
" -E, --exclude-users\n"
" Don't create the user/group tables by querying the OS when\n"
" sysdig starts. This also means that no user or group info\n"
" will be written to the trace file by the -w flag.\n"
" The user/group tables are necessary to use filter fields\n"
" like user.name or group.name. However, creating them can\n"
" increase sysdig's startup time. Moreover, they contain\n"
" information that could be privacy sensitive.\n"
" -e <num_events> If used together with -w option, creates a series of dump files\n"
" containing only a specified number of events given in num_events\n"
" parameter each.\n"
" Used alongside -W flags creates a ring buffer of file containing\n"
" num_events each.\n"
" -F, --fatfile Enable fatfile mode\n"
" when writing in fatfile mode, the output file will contain\n"
" events that will be invisible when reading the file, but\n"
" that are necessary to fully reconstruct the state.\n"
" Fatfile mode is useful when saving events to disk with an\n"
" aggressive filter. The filter could drop events that would\n"
" the state to be updated (e.g. clone() or open()). With\n"
" fatfile mode, those events are still saved to file, but\n"
" 'hidden' so that they won't appear when reading the file.\n"
" Be aware that using this flag might generate substantially\n"
" bigger traces files.\n"
" --filter-proclist apply the filter to the process table\n"
" a full dump of /proc is typically included in any trace file\n"
" to make sure all the state required to decode events is in the\n"
" file. This could cause the file to contain unwanted or sensitive\n"
" information. Using this flag causes the command line filter to\n"
" be applied to the /proc dump as well.\n"
" -g, --gvisor-config\n"
" Parse events from gVisor using the specified configuration file.\n"
" A sysdig-compatible configuration file can be generated with --gvisor-generate-config\n"
" and can be used for both runsc and sysdig.\n"
" --gvisor-generate-config [=<socket_path>(=/tmp/gvisor.sock)]\n"
" Generate a configuration file that can be used for gVisor.\n"
" --gvisor-root <gvisor_root>\n"
" gVisor root directory for storage of container state. Equivalent to runsc --root flag.\n"
" -G <num_seconds>, --seconds=<num_seconds>\n"
" Rotates the dump file specified with the -w option every\n"
" num_seconds seconds. Saved files will have the name specified\n"
" by -w which should include a time format as defined by strftime(3).\n"
" If no time format is specified, a counter will be used.\n"
" If no data format is specified, this can be used with -W flag to\n"
" create a ring buffer of events.\n"
" -h, --help Print this page\n"
" -H <pluginname>[:<initconfig>], --plugin <pluginname>[:<initconfig>]\n"
" Registers a plugin, using the passed init config if present.\n"
" The format of initconf is controlled by the plugin, refer to each\n"
" plugin's documentation to learn about it.\n"
" A path can also be used as pluginname.\n"
" -I <pluginname>[:<openparams>], --input <pluginname>[:<openparams>]\n"
" Set a previously registered plugin as input,\n"
" capturing events using it and passing the \n"
" openparams string as open parameters.\n"
" Only a single source plugin can be registered.\n"
" If no plugins were registered, any found plugin in the directories\n"
" specified by ;-separated environment variable SYSDIG_PLUGIN_DIR and\n"
" in " SYSDIG_PLUGINS_DIR " is registered; then use the provided one as input source.\n"
" The format of openparams is controlled by the plugin, refer to each\n"
" plugin's documentation to learn about it.\n"
" See https://falco.org/docs/plugins/plugin-api-reference/#ss-plugin-t-plugin-init-const-char-config-int32-t-rc-required-yes\n"
" and https://falco.org/docs/plugins/plugin-api-reference/#ss-instance-t-plugin-open-ss-plugin-t-s-const-char-params-int32-t-rc-required-yes for more infos.\n"
" The event sources available for capture vary depending on which \n"
" plugins have been installed.\n"
" -Il Lists the loaded plugins. If no plugin has been registered through '-H',\n"
" Sysdig looks for plugins in the directories \n"
" specified by ;-separated environment variable SYSDIG_PLUGIN_DIR and\n"
" in " SYSDIG_PLUGINS_DIR ".\n"
#ifdef HAS_CHISELS
" -i <chiselname>, --chisel-info <chiselname>\n"
" Get a longer description and the arguments associated with\n"
" a chisel found in the -cl option list.\n"
#endif
" -j, --json Emit output as json, data buffer encoding will depend from the\n"
" print format selected.\n"
" -L, --list-events List the events that the engine supports\n"
" -l, --list List the fields that can be used for filtering and output\n"
" formatting. Use -lv to get additional information for each\n"
" field.\n"
" --libs-version Print the falcosecurity/libs version\n"
" --large-environment\n"
" Support environments larger than 4KiB\n"
" When the environment is larger than 4KiB, load the whole\n"
" environment from /proc instead of truncating to the first 4KiB\n"
" This may fail for short-lived processes and in that case\n"
" the truncated environment is used instead.\n"
" --log-level=<trace|debug|info|notice|warning|error|critical|fatal>\n"
" Select log level. Useful together with --debug.\n"
" --list-markdown like -l, but produces markdown output\n"
" -m <url[,marathon_url]>, --mesos-api=<url[,marathon_url]>\n"
" [DEPRECATED] Enable Mesos support by connecting to the API server\n"
" specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
" Marathon url is optional and defaults to Mesos address, port 8080.\n"
" The API servers can also be specified via the environment variable\n"
" SYSDIG_MESOS_API.\n"
#ifdef HAS_MODERN_BPF
"--modern-bpf\n"
" Enable live capture using the modern BPF probe instead of the kernel module.\n"
#endif
" -M <num_seconds> Stop collecting after <num_seconds> reached.\n"
" -n <num>, --numevents=<num>\n"
" Stop capturing after <num> events\n"
" --color <true|force|false> \n"
" Set colors settings on terminal output.\n"
" --page-faults Capture user/kernel major/minor page faults\n"
" --plugin-config-file\n"
" Load the plugin configuration from a Falco-compatible yaml file.\n"
" Do not mix this option with the '-H' or '-I' options: it is unsupported.\n"
" See the plugin section in https://falco.org/docs/configuration/ for\n"
" additional information\n"
" -P, --progress Print progress on stderr while processing trace files\n"
" -p <output_format>, --print=<output_format>\n"
" Specify the format to be used when printing the events.\n"
" With -pc or -pcontainer will use a container-friendly format.\n"
" [DEPRECATED] With -pk or -pkubernetes will use a kubernetes-friendly format.\n"
" [DEPRECATED] With -pm or -pmesos will use a mesos-friendly format.\n"
" See the examples section below for more info.\n"
" --plugin-info <pluginname>\n"
" Print info for a single plugin. This includes name, author,\n"
" and all the descriptive info of the plugin. If present,\n"
" this also prints the schema format for the init configuration\n"
" and a list of suggested open parameters.\n"
" All this info is controlled by the plugin, refer to each\n"
" plugin's documentation to learn more about it.\n"
" This can be combined with the -H option to load the plugin\n"
" with a given configuration.\n"
" A path can also be used as pluginname.\n"
" -q, --quiet Don't print events on the screen\n"
" Useful when dumping to disk.\n"
" -R Resolve port numbers to names.\n"
" -r <readfile>, --read=<readfile>\n"
" Read the events from <readfile>.\n"
" -S, --summary print the event summary (i.e. the list of the top events)\n"
" when the capture ends.\n"
" -s <len>, --snaplen=<len>\n"
" Capture the first <len> bytes of each I/O buffer.\n"
" By default, the first 80 bytes are captured. Use this\n"
" option with caution, it can generate huge trace files.\n"
" -t <timetype>, --timetype=<timetype>\n"
" Change the way event time is displayed. Accepted values are\n"
" h for human-readable string, a for absolute timestamp from\n"
" epoch, r for relative time from the beginning of the\n"
" capture, d for delta between event enter and exit, and\n"
" D for delta from the previous event.\n"
" --unbuffered Turn off output buffering. This causes every single line\n"
" emitted by sysdig to be flushed, which generates higher CPU\n"
" usage but is useful when piping sysdig's output into another\n"
" process or into a script.\n"
" -U, --suppress-comm\n"
" Ignore all events from processes having the provided comm.\n"
" -v, --verbose Verbose output.\n"
" This flag will cause the full content of text and binary\n"
" buffers to be printed on screen, instead of being truncated\n"
" to 40 characters. Note that data buffers length is still\n"
" limited by the snaplen (refer to the -s flag documentation)\n"
" -v will also make sysdig print some summary information at\n"
" the end of the capture.\n"
" --version Print version number.\n"
" -w <writefile>, --write=<writefile>\n"
" Write the captured events to <writefile>.\n"
" -W <num>, --limit <num>\n"
" Used in conjunction with the -C option, this will limit the number\n"
" of files created to the specified number, and begin overwriting files\n"
" from the beginning, thus creating a 'rotating' buffer.\n"
"\n"
" Used in conjunction with the -G option, this will limit the number\n"
" of rotated dump files that get created, exiting with status 0 when\n"
" reaching the limit. If used with -C as well, the behavior will result\n"
" in cyclical files per timeslice.\n"
" -x, --print-hex Print data buffers in hex.\n"
" -X, --print-hex-ascii\n"
" Print data buffers in hex and ASCII.\n"
" -z, --compress Used with -w, enables compression for trace files.\n"
"\n"
"Output format:\n\n"
"By default, sysdig prints the information for each captured event on a single\n"
" line with the following format:\n\n"
" %%evt.num %%evt.outputtime %%evt.cpu %%proc.name (%%thread.tid) %%evt.dir %%evt.type %%evt.info\n\n"
"where:\n"
" evt.num is the incremental event number\n"
" evt.time is the event timestamp\n"
" evt.cpu is the CPU number where the event was captured\n"
" proc.name is the name of the process that generated the event\n"
" thread.tid id the TID that generated the event, which corresponds to the\n"
" PID for single thread processes\n"
" evt.dir is the event direction, > for enter events and < for exit events\n"
" evt.type is the name of the event, e.g. 'open' or 'read'\n"
" evt.info is the list of event arguments.\n\n"
"The output format can be customized with the -p switch, using any of the\n"
"fields listed by 'sysdig -l'.\n\n"
"Using -pc or -pcontainer, the default format will be changed to a container-friendly one:\n\n"
"%%evt.num %%evt.outputtime %%evt.cpu %%container.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n"
"[DEPRECATED] Using -pk or -pkubernetes, the default format will be changed to a kubernetes-friendly one:\n\n"
"%%evt.num %%evt.outputtime %%evt.cpu %%k8s.pod.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n"
"[DEPRECATED] Using -pm or -pmesos, the default format will be changed to a mesos-friendly one:\n\n"
"%%evt.num %%evt.outputtime %%evt.cpu %%mesos.task.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n"
"Examples:\n\n"
" Capture all the events from the live system and print them to screen\n"
" $ sysdig\n\n"
" Capture all the events from the live system and save them to disk\n"
" $ sysdig -w dumpfile.scap\n\n"
" Read events from a file and print them to screen\n"
" $ sysdig -r dumpfile.scap\n\n"
" Print all the open system calls invoked by cat\n"
" $ sysdig proc.name=cat and evt.type=open\n\n"
" Print the name of the files opened by cat\n"
" $ sysdig -p\"%%evt.arg.name\" proc.name=cat and evt.type=open\n\n"
" Register any found plugin and use dummy as input source passing to it open params\n"
" $ sysdig -I dummy:10'\n\n"
" Load and register dummy source plugin passing to it init config and open params\n"
" $ sysdig -H dummy:'{\"jitter\":50}' -I dummy:10\n\n"
);
}
double g_last_printed_progress_pct = 0;
char g_prg_line_buf[512] = "";
inline void clean_last_progress_line()
{
uint32_t j;
for(j = 0; j < strlen(g_prg_line_buf); j++)
{
g_prg_line_buf[j] = ' ';
}
g_prg_line_buf[j] = 0;
fprintf(stderr, "\r%s", g_prg_line_buf);
}
inline void output_progress(sinsp* inspector, sinsp_evt* ev)
{
if(ev == NULL || (ev->get_num() % 10000 == 0))
{
std::string ps;
double progress_pct = inspector->get_read_progress_with_str(&ps);
if(progress_pct - g_last_printed_progress_pct > 0.1)
{
clean_last_progress_line();
if(ps == "")
{
snprintf(g_prg_line_buf, sizeof(g_prg_line_buf), "%.2lf", progress_pct);
}
else
{
snprintf(g_prg_line_buf, sizeof(g_prg_line_buf), "%s", ps.c_str());
}
fprintf(stderr, "\r%s", g_prg_line_buf);
fflush(stderr);
g_last_printed_progress_pct = progress_pct;
}
}
}
void print_summary_table(sinsp* inspector,
std::vector<summary_table_entry> &summary_table,
uint32_t nentries)
{
std::cout << "----------------------\n";
std::string tstr = std::string("Event");
tstr.resize(16, ' ');
tstr += "#Calls\n";
std::cout << tstr;
std::cout << "----------------------\n";
sort(summary_table.begin(), summary_table.end(),
summary_table_entry_rsort_comparer());
for(uint32_t j = 0; j < nentries; j++)
{
const summary_table_entry &e = summary_table.at(j);
if(e.m_ncalls == 0)
{
break;
}
if(e.m_is_unsupported_syscall)
{
tstr = scap_get_ppm_sc_name((ppm_sc_code) (e.m_id / 2));
tstr.resize(16, ' ');
printf("%s%s%" PRIu64 "\n",
(PPME_IS_ENTER(e.m_id))? "> ": "< ",
tstr.c_str(),
e.m_ncalls);
}
else
{
tstr = libsinsp::events::info((ppm_event_code) e.m_id)->name;
tstr.resize(16, ' ');
printf("%s%s%" PRIu64 "\n",
(PPME_IS_ENTER(e.m_id))? "> ": "< ",
tstr.c_str(),
e.m_ncalls);
}
}
}
#ifdef HAS_CHISELS
static void add_chisel_dirs(sinsp* inspector)
{
//
// Add the default chisel directory statically configured by the build system
//
chisel_add_dir(SYSDIG_CHISELS_DIR, false);
//
// Add the directories configured in the SYSDIG_CHISEL_DIR environment variable
//
char* s_user_cdirs = getenv("SYSDIG_CHISEL_DIR");
if(s_user_cdirs != NULL)
{
std::vector<std::string> user_cdirs = sinsp_split(s_user_cdirs, ';');
for(uint32_t j = 0; j < user_cdirs.size(); j++)
{
chisel_add_dir(user_cdirs[j], true);
}
}
}
#endif
static void initialize_chisels()
{
#ifdef HAS_CHISELS
for(uint32_t j = 0; j < g_chisels.size(); j++)
{
g_chisels[j]->on_init();
}
#endif
}
//
// Parse the command line following a chisel to consume the chisel command line.
// We use the following strategy:
// - if the chisel has no arguments, we don't consume anything
// - if the chisel has at least one required argument, we consume the next command line token
// - if the chisel has only optional arguments, we consume the next token, unless
// - there is no next token
// - the next token starts with a '-'
// - the rest of the command line contains a valid filter
//
static void parse_chisel_args(
sinsp_chisel* ch,
std::shared_ptr<sinsp_filter_factory> filter_factory,
int optind, int argc, char **argv, int32_t* n_filterargs)
{
uint32_t nargs = ch->get_n_args();
uint32_t nreqargs = ch->get_n_required_args();
std::string args;
if(nargs != 0)
{
if(optind > (int32_t)argc)
{
throw sinsp_exception("invalid number of arguments for chisel " + std::string(optarg) + ", " + std::to_string((long long int)nargs) + " expected.");
}
else if(optind < (int32_t)argc)
{
args = argv[optind];
if(nreqargs != 0)
{
ch->set_args(args);
(*n_filterargs)++;
}
else
{
if(args[0] != '-')
{
std::string testflt;
for(int32_t j = optind; j < argc; j++)
{
testflt += argv[j];
if(j < argc - 1)
{
testflt += " ";
}
}
if(nargs == 1 && ch->get_lua_script_info()->m_args[0].m_type == "filter")
{
ch->set_args(args);
(*n_filterargs)++;
}
else
{
try
{
sinsp_filter_compiler compiler(filter_factory, testflt);
std::unique_ptr<sinsp_filter> s = compiler.compile();
}
catch(...)
{
ch->set_args(args);
(*n_filterargs)++;
}
}
}
}
}
else
{
if(nreqargs != 0)
{
throw sinsp_exception("missing arguments for chisel " + std::string(optarg));
}
}
}
}
static void free_chisels()
{
#ifdef HAS_CHISELS
for(auto & g_chisel : g_chisels)
{
delete g_chisel;
}
g_chisels.clear();
#endif
}
static void chisels_on_capture_start()
{
#ifdef HAS_CHISELS
for(auto & g_chisel : g_chisels)
{
g_chisel->on_capture_start();
}
#endif
}
static void chisels_on_capture_end()
{
#ifdef HAS_CHISELS
for(auto & g_chisel : g_chisels)
{
g_chisel->on_capture_end();
}
#endif
}
static void chisels_do_timeout(sinsp_evt* ev)
{
#ifdef HAS_CHISELS
for(std::vector<sinsp_chisel*>::iterator it = g_chisels.begin();
it != g_chisels.end(); ++it)
{
(*it)->do_timeout(ev);
}
#endif
}
void handle_end_of_file(sinsp* inspector, bool print_progress, bool reset_colors = false, sinsp_evt_formatter* formatter = NULL)
{
std::string line;
if(reset_colors) {
std::cout << "\e[00m";
}
//
// Reached the end of a trace file.
// If we are reporting progress, this is 100%
//
if(print_progress)
{
clean_last_progress_line();
if(inspector == NULL)
{
fprintf(stderr, "\r100.00\n");
}
else
{
output_progress(inspector, NULL);
fprintf(stderr, "\n");
}
fflush(stderr);
}
//
// Notify the chisels that we're exiting.
//
try
{
chisels_on_capture_end();
}
catch(...)
{
}
}
std::vector<std::string> split_nextrun_args(std::string na)
{
std::vector<std::string> res;
uint32_t laststart = 0;
uint32_t j;
bool inquote = false;
for(j = 0; j < na.size(); j++)
{
if(na[j] == '"')
{
inquote = !inquote;
}
else if(na[j] == ' ')
{
if(!inquote)
{
std::string arg = na.substr(laststart, j - laststart);
replace_in_place(arg, "\"", "");
res.push_back(arg);
laststart = j + 1;
}
}
}
res.push_back(na.substr(laststart, j - laststart));
laststart = j + 1;
return res;
}
//
// Event processing loop
//
captureinfo do_inspect(sinsp *inspector, sinsp_cycledumper *dumper,
uint64_t cnt, uint64_t duration_to_tot_ns, bool quiet,
bool json, bool do_flush, bool reset_colors,
bool print_progress,
std::unique_ptr<sinsp_filter> display_filter,
std::vector<summary_table_entry> &summary_table,
sinsp_evt_formatter *syscall_evt_formatter,
sinsp_evt_formatter *plugin_evt_formatter,
std::shared_ptr<sinsp_syslog_decoder> syslog_decoder)
{
captureinfo retval;
int32_t res;
sinsp_evt* ev;
std::string line;
uint64_t duration_start = 0;
if(json)
{
do_flush = true;
}
// This changes between syscall_evt_formatter and
// plugin_evt_formatter based on the event type.
sinsp_evt_formatter *formatter = syscall_evt_formatter;
inspector->start_capture();
//
// Loop through the events
//
while(1)
{
if(retval.m_nevts == cnt || g_terminate)
{
//
// End of capture, either because the user stopped it, or because
// we reached the event count specified with -n.
//
if(g_terminate)
{
g_terminating = true;
}
handle_end_of_file(inspector, print_progress, reset_colors, formatter);
break;
}
syslog_decoder->reset();
res = inspector->next(&ev);
if (ev)
{
const uint16_t etype = ev->get_scap_evt()->type;
if (etype == PPME_SYSCALL_WRITE_X || etype == PPME_SYSCALL_WRITEV_X ||
etype == PPME_SYSCALL_PWRITE_X || etype == PPME_SYSCALL_PWRITEV_X ||
etype == PPME_SOCKET_SEND_X || etype == PPME_SOCKET_SENDTO_X ||
etype == PPME_SOCKET_SENDMSG_X || etype == PPME_SOCKET_SENDMMSG_X)
{
syslog_decoder->parse(ev);
}
}
if(dumper && ev && res != SCAP_EOF)
{
dumper->dump(ev);
}
else if(res == SCAP_FAILURE && !inspector->is_live())
{
//
// scap file truncated.
//
// We fail gracefully:
// - all the expected output (except truncated
// events) will be on stdout
// - the return code will be set as success
// - the inspector error will be on stderr
//
handle_end_of_file(inspector, print_progress, reset_colors, formatter);
std::cerr << inspector->getlasterr() << std::endl;
break;
}
if(res == SCAP_TIMEOUT || res == SCAP_FILTERED_EVENT)
{
if(res == SCAP_FILTERED_EVENT && ev != NULL && ev->is_filtered_out())
{
//
// The event has been dropped by the filtering system.
// Give the chisels a chance to run their timeout logic.
//
chisels_do_timeout(ev);
if(print_progress)
{
output_progress(inspector, ev);
}
}
continue;
}
else if(res == SCAP_EOF)
{
handle_end_of_file(inspector, print_progress, reset_colors, formatter);
break;
}
else if(res != SCAP_SUCCESS)
{
//
// Event read error.
// Notify the chisels that we're exiting, and then die with an error.
//
handle_end_of_file(inspector, print_progress, reset_colors, formatter);
std::cerr << "res = " << res << std::endl;
throw sinsp_exception(inspector->getlasterr().c_str());
}
formatter = (ev->get_type() == PPME_PLUGINEVENT_E ? plugin_evt_formatter : syscall_evt_formatter);
if (duration_start == 0)
{
duration_start = ev->get_ts();
} else if(duration_to_tot_ns > 0)
{
if(ev->get_ts() - duration_start >= duration_to_tot_ns)
{
handle_end_of_file(inspector, print_progress, reset_colors, formatter);
break;
}
}
retval.m_nevts++;
if(print_progress)
{
output_progress(inspector, ev);
}
//
// If there are chisels to run, run them
//
#ifdef HAS_CHISELS
if(!g_chisels.empty())
{
for(const auto& chisel : g_chisels)
{
if(chisel->run(ev) == false)
{
continue;
}
}
}
else
#endif
{
//
// If we're supposed to summarize, increase the count for this event
//
if(!summary_table.empty())
{
uint16_t etype = ev->get_type();
if(etype == PPME_GENERIC_E)
{
const sinsp_evt_param *parinfo = ev->get_param(0);
uint16_t id = *(int16_t *)parinfo->m_val;
summary_table[PPM_EVENT_MAX + id * 2].m_ncalls++;
}
else if(etype == PPME_GENERIC_X)
{
const sinsp_evt_param *parinfo = ev->get_param(0);
uint16_t id = *(int16_t *)parinfo->m_val;
summary_table[PPM_EVENT_MAX + id * 2 + 1].m_ncalls++;
}
else
{
summary_table[etype].m_ncalls++;
}
}
//
// When the quiet flag is specified, we don't do any kind of processing other
// than counting the events.
//
if(quiet)
{
continue;
}
if(!inspector->is_debug_enabled() &&
ev->get_category() & EC_INTERNAL)
{
continue;
}
if(display_filter.get() && !display_filter->run(ev))
{
continue;
}
//
// Output the line
//
if(formatter->tostring(ev, &line))
{
std::cout << line << std::endl;
}
}
if(do_flush)
{
std::cout << std::flush;
}
}
inspector->stop_capture();
return retval;
}
#ifndef _WIN32
struct termios g_saved_term_attributes;
void reset_tty_input_mode(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &g_saved_term_attributes);
}
void disable_tty_echo() {
struct termios tattr;
/* Make sure stdin is a terminal. */
if (!isatty(STDIN_FILENO))
{
return;
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr(STDIN_FILENO, &g_saved_term_attributes);
atexit(reset_tty_input_mode);
/* Disable terminal echo */
tcgetattr(STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
}
#endif
static inline bool stdout_supports_color()
{
#ifdef _WIN32
return false;
#else
return isatty(fileno(stdout));
#endif
}
std::string escape_output_format(const std::string& s)
{
std::stringstream ss{""};
for(size_t i = 0; i < s.length(); i++)
{
if (s.at(i) == '\\')
{
switch(s.at(i + 1))
{
case 'n': ss << "\n"; i++; break;
case 't': ss << "\t"; i++; break;
case 'e': ss << "\e"; i++; break;
case '\\': ss << "\\"; i++; break;
default: ss << "\\"; break;
}
}
else
{
ss << s.at(i);
}
}
return ss.str();
}
//
// ARGUMENT PARSING AND PROGRAM SETUP
//
sysdig_init_res sysdig_init(int argc, char **argv)
{
sysdig_init_res res;
std::unique_ptr<sinsp> inspector;
std::unique_ptr<sinsp_cycledumper> dumper;
std::vector<std::string> infiles;
std::string outfile;
int op;
uint64_t cnt = -1;
bool quiet = false;
bool is_filter_display = false;
bool verbose = false;
bool list_flds = false;
bool list_flds_markdown = false;
std::string list_flds_source = "";
bool compress = false;
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
std::unique_ptr<sinsp_filter> display_filter;
double duration = 1;
int duration_to_tot = 0;