1 /*
2     pmacct (Promiscuous mode IP Accounting package)
3     pmacct is Copyright (C) 2003-2020 by Paolo Lucente
4 */
5 
6 /*
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 
22 /* includes */
23 #include "pmacct.h"
24 #include "addr.h"
25 #include "plugin_hooks.h"
26 #include "bgp/bgp.h"
27 #include "bgp/bgp_lg.h"
28 #include "pmbgpd.h"
29 #include "pretag_handlers.h"
30 #include "pmacct-data.h"
31 #include "pkt_handlers.h"
32 #include "ip_flow.h"
33 #include "classifier.h"
34 #include "net_aggr.h"
35 #include "thread_pool.h"
36 
37 /* global var */
38 extern struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */
39 
40 /* Functions */
usage_daemon(char * prog_name)41 void usage_daemon(char *prog_name)
42 {
43   printf("%s %s (%s)\n", PMBGPD_USAGE_HEADER, PMACCT_VERSION, PMACCT_BUILD);
44   printf("Usage: %s [ -D | -d ] [ -L IP address ] [ -l port ] ]\n", prog_name);
45   printf("       %s [ -f config_file ]\n", prog_name);
46   printf("       %s [ -h ]\n", prog_name);
47   printf("\nGeneral options:\n");
48   printf("  -h  \tShow this page\n");
49   printf("  -V  \tShow version and compile-time options and exit\n");
50   printf("  -L  \tBind to the specified IP address\n");
51   printf("  -l  \tListen on the specified TCP port\n");
52   printf("  -f  \tLoad configuration from the specified file\n");
53   printf("  -D  \tDaemonize\n");
54   printf("  -d  \tEnable debug\n");
55   printf("  -S  \t[ auth | mail | daemon | kern | user | local[0-7] ] \n\tLog to the specified syslog facility\n");
56   printf("  -F  \tWrite Core Process PID into the specified file\n");
57   printf("  -o  \tOutput file to log real-time BGP messages\n");
58   printf("  -O  \tOutput file to dump generated RIBs at regular time intervals\n");
59   printf("  -i  \tInterval, in secs, to write to the dump output file (supplied by -O)\n");
60   printf("  -g  \tEnable the Looking Glass server\n");
61   printf("  -m  \tLoad a BGP xconnects map from the specified file\n");
62   printf("\n");
63   printf("For examples, see:\n");
64   printf("  https://github.com/pmacct/pmacct/blob/master/QUICKSTART or\n");
65   printf("  https://github.com/pmacct/pmacct/wiki\n");
66   printf("\n");
67   printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER);
68 }
69 
main(int argc,char ** argv,char ** envp)70 int main(int argc,char **argv, char **envp)
71 {
72   struct plugins_list_entry *list;
73   char config_file[SRVBUFLEN];
74   int logf;
75 
76   /* getopt() stuff */
77   extern char *optarg;
78   extern int optind, opterr, optopt;
79   int errflag, cp;
80 
81 #ifdef WITH_REDIS
82   struct p_redis_host redis_host;
83 #endif
84 
85 #if defined HAVE_MALLOPT
86   mallopt(M_CHECK_ACTION, 0);
87 #endif
88 
89   umask(077);
90 
91   memset(cfg_cmdline, 0, sizeof(cfg_cmdline));
92   memset(&config, 0, sizeof(struct configuration));
93   memset(&config_file, 0, sizeof(config_file));
94   memset(empty_mem_area_256b, 0, sizeof(empty_mem_area_256b));
95 
96   log_notifications_init(&log_notifications);
97   config.acct_type = ACCT_PMBGP;
98 
99   find_id_func = NULL;
100   plugins_list = NULL;
101   errflag = 0;
102   rows = 0;
103 
104   /* getting commandline values */
105   while (!errflag && ((cp = getopt(argc, argv, ARGS_PMBGPD)) != -1)) {
106     cfg_cmdline[rows] = malloc(SRVBUFLEN);
107     switch (cp) {
108     case 'L':
109       strlcpy(cfg_cmdline[rows], "bgp_daemon_ip: ", SRVBUFLEN);
110       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
111       rows++;
112       break;
113     case 'l':
114       strlcpy(cfg_cmdline[rows], "bgp_daemon_port: ", SRVBUFLEN);
115       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
116       rows++;
117       break;
118     case 'D':
119       strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN);
120       rows++;
121       break;
122     case 'd':
123       debug = TRUE;
124       strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN);
125       rows++;
126       break;
127     case 'f':
128       strlcpy(config_file, optarg, sizeof(config_file));
129       break;
130     case 'F':
131       strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN);
132       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
133       rows++;
134       break;
135     case 'S':
136       strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN);
137       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
138       rows++;
139       break;
140     case 'o':
141       strlcpy(cfg_cmdline[rows], "bgp_daemon_msglog_file: ", SRVBUFLEN);
142       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
143       rows++;
144       break;
145     case 'O':
146       strlcpy(cfg_cmdline[rows], "bgp_table_dump_file: ", SRVBUFLEN);
147       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
148       rows++;
149       break;
150     case 'i':
151       strlcpy(cfg_cmdline[rows], "bgp_table_dump_refresh_time: ", SRVBUFLEN);
152       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
153       rows++;
154       break;
155     case 'g':
156       strlcpy(cfg_cmdline[rows], "bgp_daemon_lg: true", SRVBUFLEN);
157       rows++;
158       break;
159     case 'm':
160       strlcpy(cfg_cmdline[rows], "bgp_daemon_xconnect_map: ", SRVBUFLEN);
161       strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows]));
162       rows++;
163       break;
164     case 'h':
165       usage_daemon(argv[0]);
166       exit(0);
167       break;
168     case 'V':
169       version_daemon(PMBGPD_USAGE_HEADER);
170       exit(0);
171       break;
172     default:
173       usage_daemon(argv[0]);
174       exit(1);
175       break;
176     }
177   }
178 
179   /* post-checks and resolving conflicts */
180   if (strlen(config_file)) {
181     if (parse_configuration_file(config_file) != SUCCESS)
182       exit(1);
183   }
184   else {
185     if (parse_configuration_file(NULL) != SUCCESS)
186       exit(1);
187   }
188 
189   list = plugins_list;
190   while (list) {
191     list->cfg.acct_type = ACCT_PMBGP;
192     set_default_preferences(&list->cfg);
193     if (!strcmp(list->type.string, "core")) {
194       memcpy(&config, &list->cfg, sizeof(struct configuration));
195       config.name = list->name;
196       config.type = list->type.string;
197     }
198     list = list->next;
199   }
200 
201   if (config.files_umask) umask(config.files_umask);
202 
203   initsetproctitle(argc, argv, envp);
204   if (config.syslog) {
205     logf = parse_log_facility(config.syslog);
206     if (logf == ERR) {
207       config.syslog = NULL;
208       printf("WARN ( %s/core ): specified syslog facility is not supported. Logging to standard error (stderr).\n", config.name);
209     }
210     else openlog(NULL, LOG_PID, logf);
211     Log(LOG_INFO, "INFO ( %s/core ): Start logging ...\n", config.name);
212   }
213 
214   if (config.logfile) {
215     config.logfile_fd = open_output_file(config.logfile, "a", FALSE);
216     while (list) {
217       list->cfg.logfile_fd = config.logfile_fd ;
218       list = list->next;
219     }
220   }
221 
222   if (config.daemon) {
223     if (!config.syslog && !config.logfile) {
224       if (debug || config.debug) {
225 	printf("WARN ( %s/core ): debug is enabled; forking in background. Logging to standard error (stderr) will get lost.\n", config.name);
226       }
227     }
228 
229     daemonize();
230   }
231 
232   if (config.proc_priority) {
233     int ret;
234 
235     ret = setpriority(PRIO_PROCESS, 0, config.proc_priority);
236     if (ret) Log(LOG_WARNING, "WARN ( %s/core ): proc_priority failed (errno: %d)\n", config.name, errno);
237     else Log(LOG_INFO, "INFO ( %s/core ): proc_priority set to %d\n", config.name, getpriority(PRIO_PROCESS, 0));
238   }
239 
240   Log(LOG_INFO, "INFO ( %s/core ): %s %s (%s)\n", config.name, PMBGPD_USAGE_HEADER, PMACCT_VERSION, PMACCT_BUILD);
241   Log(LOG_INFO, "INFO ( %s/core ): %s\n", config.name, PMACCT_COMPILE_ARGS);
242 
243   if (strlen(config_file)) {
244     char canonical_path[PATH_MAX], *canonical_path_ptr;
245 
246     canonical_path_ptr = realpath(config_file, canonical_path);
247     if (canonical_path_ptr) Log(LOG_INFO, "INFO ( %s/core ): Reading configuration file '%s'.\n", config.name, canonical_path);
248   }
249   else Log(LOG_INFO, "INFO ( %s/core ): Reading configuration from cmdline.\n", config.name);
250 
251   pm_setproctitle("%s [%s]", "Core Process", config.proc_name);
252   if (config.pidfile) write_pid_file(config.pidfile);
253 
254   /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */
255   memset(&sighandler_action, 0, sizeof(sighandler_action)); /* To ensure the struct holds no garbage values */
256   sigemptyset(&sighandler_action.sa_mask);  /* Within a signal handler all the signals are enabled */
257   sighandler_action.sa_flags = SA_RESTART;  /* To enable re-entering a system call afer done with signal handling */
258 
259   sighandler_action.sa_handler = startup_handle_falling_child;
260   sigaction(SIGCHLD, &sighandler_action, NULL);
261 
262   /* handles reopening of syslog channel */
263   sighandler_action.sa_handler = reload;
264   sigaction(SIGHUP, &sighandler_action, NULL);
265 
266   /* logs various statistics via Log() calls */
267   sighandler_action.sa_handler = SIG_IGN;
268   sigaction(SIGUSR1, &sighandler_action, NULL);
269 
270   /* sets to true the reload_maps flag */
271   sighandler_action.sa_handler = reload_maps;
272   sigaction(SIGUSR2, &sighandler_action, NULL);
273 
274   /* we want to exit gracefully when a pipe is broken */
275   sighandler_action.sa_handler = SIG_IGN;
276   sigaction(SIGPIPE, &sighandler_action, NULL);
277 
278   sighandler_action.sa_handler = PM_sigint_handler;
279   sigaction(SIGINT, &sighandler_action, NULL);
280 
281   sighandler_action.sa_handler = PM_sigint_handler;
282   sigaction(SIGTERM, &sighandler_action, NULL);
283 
284   sighandler_action.sa_handler = handle_falling_child;
285   sigaction(SIGCHLD, &sighandler_action, NULL);
286 
287   sighandler_action.sa_handler = PM_sigalrm_noop_handler;
288   sigaction(SIGALRM, &sighandler_action, NULL);
289 
290   if (!config.bgp_daemon) config.bgp_daemon = BGP_DAEMON_ONLINE;
291   if (!config.bgp_daemon_port) config.bgp_daemon_port = BGP_TCP_PORT;
292 
293 #if defined WITH_ZMQ
294   if (config.bgp_lg) bgp_lg_wrapper();
295 #else
296   if (config.bgp_lg) {
297     Log(LOG_ERR, "ERROR ( %s/core/lg ): 'bgp_daemon_lg' requires --enable-zmq. Exiting.\n", config.name);
298     exit_gracefully(1);
299   }
300 #endif
301 
302 #ifdef WITH_REDIS
303   if (config.redis_host) {
304     char log_id[SHORTBUFLEN];
305 
306     snprintf(log_id, sizeof(log_id), "%s/%s", config.name, config.type);
307     p_redis_init(&redis_host, log_id, p_redis_thread_produce_common_core_handler);
308   }
309 #endif
310 
311   bgp_prepare_daemon();
312   skinny_bgp_daemon();
313 
314   return 0;
315 }
316