sharkd: squelch redundant decl warnings
[metze/wireshark/wip.git] / sharkd_daemon.c
1 /* sharkd_daemon.c
2  *
3  * Copyright (C) 2016 Jakub Zawadzki
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11
12 #include <config.h>
13
14 #include <glib.h>
15
16 #include <stdio.h>
17 #include <errno.h>
18 #include <stddef.h>
19 #include <stdlib.h>
20 #include <signal.h>
21
22 #ifdef _WIN32
23 #include <wsutil/unicode-utils.h>
24 #include <wsutil/win32-utils.h>
25 #endif
26
27 #include <wsutil/filesystem.h>
28 #include <wsutil/socket.h>
29 #include <wsutil/inet_addr.h>
30 #include <wsutil/please_report_bug.h>
31 /*
32  * If we have getopt_long() in the system library, include <getopt.h>.
33  * Otherwise, we're using our own getopt_long() (either because the
34  * system has getopt() but not getopt_long(), as with some UN*Xes,
35  * or because it doesn't even have getopt(), as with Windows), so
36  * include our getopt_long()'s header.
37  */
38 #ifdef HAVE_GETOPT_LONG
39 #include <getopt.h>
40 #else
41 #include <wsutil/wsgetopt.h>
42 #endif
43
44 #ifndef _WIN32
45 #include <sys/un.h>
46 #include <netinet/tcp.h>
47 #endif
48
49 #include <wsutil/strtoi.h>
50 #include <version_info.h>
51
52 #include "sharkd.h"
53
54 #ifdef _WIN32
55 /* for windows support TCP sockets */
56 # define SHARKD_TCP_SUPPORT
57 #else
58 /* for other system support only local sockets */
59 # define SHARKD_UNIX_SUPPORT
60 #endif
61
62 static int mode = 0;
63 static socket_handle_t _server_fd = INVALID_SOCKET;
64
65 static socket_handle_t
66 socket_init(char *path)
67 {
68         socket_handle_t fd = INVALID_SOCKET;
69         char *err_msg;
70
71         err_msg = ws_init_sockets();
72         if (err_msg != NULL) {
73                 g_warning("ERROR: %s", err_msg);
74                 g_free(err_msg);
75                 g_warning("%s", please_report_bug());
76                 return fd;
77         }
78
79 #ifdef SHARKD_UNIX_SUPPORT
80         if (!strncmp(path, "unix:", 5))
81         {
82                 struct sockaddr_un s_un;
83                 socklen_t s_un_len;
84
85                 path += 5;
86
87                 if (strlen(path) + 1 > sizeof(s_un.sun_path))
88                         return INVALID_SOCKET;
89
90                 fd = socket(AF_UNIX, SOCK_STREAM, 0);
91                 if (fd == INVALID_SOCKET)
92                         return INVALID_SOCKET;
93
94                 memset(&s_un, 0, sizeof(s_un));
95                 s_un.sun_family = AF_UNIX;
96                 g_strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path));
97
98                 s_un_len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen(s_un.sun_path));
99
100                 if (s_un.sun_path[0] == '@')
101                         s_un.sun_path[0] = '\0';
102
103                 if (bind(fd, (struct sockaddr *) &s_un, s_un_len))
104                 {
105                         closesocket(fd);
106                         return INVALID_SOCKET;
107                 }
108         }
109         else
110 #endif
111
112 #ifdef SHARKD_TCP_SUPPORT
113         if (!strncmp(path, "tcp:", 4))
114         {
115                 struct sockaddr_in s_in;
116                 int one = 1;
117                 char *port_sep;
118                 guint16 port;
119
120                 path += 4;
121
122                 port_sep = strchr(path, ':');
123                 if (!port_sep)
124                         return INVALID_SOCKET;
125
126                 *port_sep = '\0';
127
128                 if (ws_strtou16(port_sep + 1, NULL, &port) == FALSE)
129                         return INVALID_SOCKET;
130
131 #ifdef _WIN32
132                 /* Need to use WSASocket() to disable overlapped I/O operations,
133                    this way on windows SOCKET can be used as HANDLE for stdin/stdout */
134                 fd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
135 #else
136                 fd = socket(AF_INET, SOCK_STREAM, 0);
137 #endif
138                 if (fd == INVALID_SOCKET)
139                         return INVALID_SOCKET;
140
141                 s_in.sin_family = AF_INET;
142                 ws_inet_pton4(path, &(s_in.sin_addr.s_addr));
143                 s_in.sin_port = g_htons(port);
144                 *port_sep = ':';
145
146                 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
147
148                 if (bind(fd, (struct sockaddr *) &s_in, sizeof(struct sockaddr_in)))
149                 {
150                         closesocket(fd);
151                         return INVALID_SOCKET;
152                 }
153         }
154         else
155 #endif
156         {
157                 return INVALID_SOCKET;
158         }
159
160         if (listen(fd, SOMAXCONN))
161         {
162                 closesocket(fd);
163                 return INVALID_SOCKET;
164         }
165
166         return fd;
167 }
168
169 static void
170 print_usage(FILE* output)
171 {
172         fprintf(output, "\n");
173         fprintf(output, "Usage: sharkd [<classic_options>|<gold_options>]\n");
174
175         fprintf(output, "\n");
176         fprintf(output, "Classic (classic_options):\n");
177         fprintf(output, "  [-|<socket>]\n");
178         fprintf(output, "\n");
179         fprintf(output, "  <socket> examples:\n");
180 #ifdef SHARKD_UNIX_SUPPORT
181         fprintf(output, "  - unix:/tmp/sharkd.sock - listen on unix file /tmp/sharkd.sock\n");
182 #endif
183 #ifdef SHARKD_TCP_SUPPORT
184         fprintf(output, "  - tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
185 #endif
186
187         fprintf(output, "\n");
188         fprintf(output, "Gold (gold_options):\n");
189         fprintf(output, "  -a <socket>, --api <socket>\n");
190         fprintf(output, "                           listen on this socket\n");
191         fprintf(output, "  -h, --help               show this help information\n");
192         fprintf(output, "  -v, --version            show version information\n");
193         fprintf(output, "  -C <config profile>, --config-profile <config profile>\n");
194         fprintf(output, "                           start with specified configuration profile\n");
195
196         fprintf(output, "\n");
197         fprintf(output, "  Examples:\n");
198         fprintf(output, "    sharkd -C myprofile\n");
199         fprintf(output, "    sharkd -a tcp:127.0.0.1:4446 -C myprofile\n");
200
201         fprintf(output, "\n");
202         fprintf(output, "See the sharkd page of the Wireshark wiki for full details.\n");
203         fprintf(output, "\n");
204 }
205
206 int
207 sharkd_init(int argc, char **argv)
208 {
209         /*
210          * The leading + ensures that getopt_long() does not permute the argv[]
211          * entries.
212          *
213          * We have to make sure that the first getopt_long() preserves the content
214          * of argv[] for the subsequent getopt_long() call.
215          *
216          * We use getopt_long() in both cases to ensure that we're using a routine
217          * whose permutation behavior we can control in the same fashion on all
218          * platforms, and so that, if we ever need to process a long argument before
219          * doing further initialization, we can do so.
220          *
221          * Glibc and Solaris libc document that a leading + disables permutation
222          * of options, regardless of whether POSIXLY_CORRECT is set or not; *BSD
223          * and macOS don't document it, but do so anyway.
224          *
225          * We do *not* use a leading - because the behavior of a leading - is
226          * platform-dependent.
227          */
228
229 #define OPTSTRING "+" "a:hmvC:"
230
231         static const char    optstring[] = OPTSTRING;
232
233         // right now we don't have any long options
234         static const struct option long_options[] = {
235           {"api", required_argument, NULL, 'a'},
236           {"help", no_argument, NULL, 'h'},
237           {"version", no_argument, NULL, 'v'},
238           {"config-profile", required_argument, NULL, 'C'},
239           {0, 0, 0, 0 }
240         };
241
242         int opt;
243
244 #ifndef _WIN32
245         pid_t pid;
246 #endif
247         socket_handle_t fd;
248
249         if (argc < 2)
250         {
251                 print_usage(stderr);
252                 return -1;
253         }
254
255         // check for classic command line
256         if (!strcmp(argv[1], "-") || argv[1][0] == 't' || argv[1][0] == 'u')
257         {
258                 mode = SHARKD_MODE_CLASSIC_CONSOLE;
259
260 #ifndef _WIN32
261                 signal(SIGCHLD, SIG_IGN);
262 #endif
263
264                 if (!strcmp(argv[1], "-"))
265                 {
266                         mode = SHARKD_MODE_CLASSIC_CONSOLE;
267                 }
268                 else
269                 {
270                         fd = socket_init(argv[1]);
271                         if (fd == INVALID_SOCKET)
272                                 return -1;
273                         _server_fd = fd;
274                         mode = SHARKD_MODE_CLASSIC_DAEMON;
275                 }
276         }
277         else
278                 mode = SHARKD_MODE_GOLD_CONSOLE;  // assume we are running as gold console
279
280         if (mode >= SHARKD_MODE_GOLD_CONSOLE)
281         {
282                 /*
283                         In Daemon Mode, we will come through here twice; once when we start the Daemon and
284                         once again after we have forked the session process.  The second time through, the
285                         session process has already had its stdin and stdout wired up to the TCP or UNIX
286                         socket and so in the orignal version of sharkd the session process is invoked with
287                         the command line: sharkd -
288
289                         When not using the classic command line, we want to spawn the session process with
290                         the complete command line with all the new options but with the -a option and
291                         parameter removed.  Invoking a second time with the -a option will cause a loop
292                         where we repeatedly spawn a new session process.
293                 */
294
295                 do {
296                         if (optind > (argc - 1))
297                                 break;
298
299                         opt = getopt_long(argc, argv, optstring, long_options, NULL);
300
301                         switch (opt) {
302                         case 'C':        /* Configuration Profile */
303                                 if (profile_exists(optarg, FALSE)) {
304                                         set_profile_name(optarg);  // In Daemon Mode, we may need to do this again in the child process
305                                 }
306                                 else {
307                                         fprintf(stderr, "Configuration Profile \"%s\" does not exist\n", optarg);
308                                         return -1;
309                                 }
310                                 break;
311
312                         case 'a':
313                                 fd = socket_init(optarg);
314                                 if (fd == INVALID_SOCKET)
315                                         return -1;
316                                 _server_fd = fd;
317
318                                 fprintf(stderr, "Sharkd listening on: %s\n", optarg);
319
320                                 mode = SHARKD_MODE_GOLD_DAEMON;
321                                 break;
322
323                         case 'h':
324                                 print_usage(stderr);
325                                 exit(0);
326                                 break;
327
328                         case 'm':
329                                 // m is an internal-only option used when the daemon session process is created
330                                 mode = SHARKD_MODE_GOLD_CONSOLE;
331                                 break;
332
333                         case 'v':         /* Show version and exit */
334                                 show_version();
335                                 exit(0);
336                                 break;
337
338                         default:
339                                 if (!optopt)
340                                         fprintf(stderr, "This option isn't supported: %s\n", argv[optind]);
341                                 fprintf(stderr, "Use sharkd -h for details of supported options\n");
342                                 exit(0);
343                                 break;
344                         }
345                 } while (opt != -1);
346         }
347
348         if (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON)
349         {
350                 /* all good - try to daemonize */
351 #ifndef _WIN32
352                 pid = fork();
353                 if (pid == -1)
354                         fprintf(stderr, "cannot go to background fork() failed: %s\n", g_strerror(errno));
355
356                 if (pid != 0)
357                 {
358                         /* parent */
359                         exit(0);
360                 }
361 #endif
362         }
363
364         return 0;
365 }
366
367 int
368 #ifndef _WIN32
369 sharkd_loop(int argc _U_, char* argv[] _U_)
370 #else
371 sharkd_loop(int argc _U_, char* argv[])
372 #endif
373 {
374         if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
375         {
376                 return sharkd_session_main(mode);
377         }
378
379         while (1)
380         {
381 #ifndef _WIN32
382                 pid_t pid;
383 #else
384                 PROCESS_INFORMATION pi;
385                 STARTUPINFO si;
386                 char *exename;
387                 char command_line[2048];
388 #endif
389                 socket_handle_t fd;
390
391                 fd = accept(_server_fd, NULL, NULL);
392                 if (fd == INVALID_SOCKET)
393                 {
394                         fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
395                         continue;
396                 }
397
398                 /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
399 #ifndef _WIN32
400                 pid = fork();
401                 if (pid == 0)
402                 {
403                         closesocket(_server_fd);
404                         /* redirect stdin, stdout to socket */
405                         dup2(fd, 0);
406                         dup2(fd, 1);
407                         close(fd);
408
409                         exit(sharkd_session_main(mode));
410                 }
411
412                 if (pid == -1)
413                 {
414                         fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
415                 }
416
417 #else
418                 memset(&pi, 0, sizeof(pi));
419                 memset(&si, 0, sizeof(si));
420
421                 si.cb = sizeof(si);
422                 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
423                 si.hStdInput = (HANDLE) fd;
424                 si.hStdOutput = (HANDLE) fd;
425                 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
426
427                 exename = g_strdup_printf("%s\\%s", get_progfile_dir(), "sharkd.exe");
428
429                 // we need to pass in all of the command line parameters except the -a parameter
430                 // passing in -a at this point would could a loop, each iteration of which would generate a new session process
431                 memset(&command_line, 0, sizeof(command_line));
432
433                 if (mode <= SHARKD_MODE_CLASSIC_DAEMON)
434                 {
435                         g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
436                 }
437                 else
438                 {
439                         // The -m option used here is an internal-only option that notifies the child process that it should
440                         // run in Gold Console mode
441                         g_strlcat(command_line, "sharkd.exe -m", sizeof(command_line));
442
443                         for (size_t i = 1; i < argc; i++)
444                         {
445                                 if (
446                                         !g_ascii_strncasecmp(argv[i], "-a", (guint)strlen(argv[i]))
447                                         || !g_ascii_strncasecmp(argv[i], "--api", (guint)strlen(argv[i]))
448                                         )
449                                 {
450                                         i++;  // skip the socket details
451                                 }
452                                 else
453                                 {
454                                         g_strlcat(command_line, " ", sizeof(command_line));
455                                         g_strlcat(command_line, argv[i], sizeof(command_line));
456                                 }
457                         }
458                 }
459
460                 if (!win32_create_process(exename, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
461                 {
462                         fprintf(stderr, "win32_create_process(%s) failed\n", exename);
463                 }
464                 else
465                 {
466                         CloseHandle(pi.hThread);
467                 }
468
469                 g_free(exename);
470 #endif
471
472                 closesocket(fd);
473         }
474         return 0;
475 }
476
477 /*
478  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
479  *
480  * Local variables:
481  * c-basic-offset: 8
482  * tab-width: 8
483  * indent-tabs-mode: t
484  * End:
485  *
486  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
487  * :indentSize=8:tabSize=8:noTabs=false:
488  */