Some random changes.
[oweals/tinc.git] / tincd.c
1 /*
2     tincd.c -- the main file for tincd
3
4     Copyright (C) 2000-2004 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id$
21 */
22
23 #include "system.h"
24
25 #include <getopt.h>
26
27 #include "cfg/cfg.h"
28 #include "fd/event.h"
29 #include "fd/fd.h"
30 #include "logger/logger.h"
31 #include "support/avl.h"
32 #include "support/sockaddr.h"
33 #include "support/xalloc.h"
34 #include "tnl/tnl.h"
35 #include "vnd/vnd.h"
36
37 static bool show_help = false;
38 static bool show_version = false;
39 static int kill_tincd = 0;
40 static bool bypass_security = false;
41 static bool do_mlock = false;
42 static bool use_logfile = false;
43 static bool do_detach = true;
44 static int debug_level = 1;
45
46 static char *confbase = NULL;   
47 static char *identname = NULL;  
48 static char *pidfilename = NULL;
49 static char *logfilename = NULL;
50 static char *cfgfilename = NULL;
51
52 int tinc_argc;
53 char **tinc_argv;
54 cfg_tree_t tinc_cfg;
55
56 static struct option const long_options[] = {
57         {"config", required_argument, NULL, 'c'},
58         {"kill", optional_argument, NULL, 'k'},
59         {"net", required_argument, NULL, 'n'},
60         {"help", no_argument, NULL, 1},
61         {"version", no_argument, NULL, 2},
62         {"no-detach", no_argument, NULL, 'D'},
63         {"debug", optional_argument, NULL, 'd'},
64         {"bypass-security", no_argument, NULL, 3},
65         {"mlock", no_argument, NULL, 'L'},
66         {"logfile", optional_argument, NULL, 4},
67         {"pidfile", required_argument, NULL, 5},
68         {NULL, 0, NULL, 0}
69 };
70
71 #ifdef HAVE_MINGW
72 static struct WSAData wsa_state;
73 #endif
74
75 static void usage(bool status) {
76         if(status)
77                 fprintf(stderr, _("Try `%s --help\' for more information.\n"), tinc_argv[0]);
78         else {
79                 printf(_("Usage: %s [option]...\n\n"), tinc_argv[0]);
80                 printf(_("  -c, --config=DIR           Read configuration options from DIR.\n"
81                                 "  -D, --no-detach            Don't fork and detach.\n"
82                                 "  -d, --debug[=LEVEL]        Increase debug level or set it to LEVEL.\n"
83                                 "  -k, --kill[=SIGNAL]        Attempt to kill a running tincd and exit.\n"
84                                 "  -n, --net=NETNAME          Connect to net NETNAME.\n"
85                                 "  -L, --mlock                Lock tinc into main memory.\n"
86                                 "      --logfile[=FILENAME]   Write log entries to a logfile.\n"
87                                 "      --pidfile=FILENAME     Write PID to FILENAME.\n"
88                                 "      --help                 Display this help and exit.\n"
89                                 "      --version              Output version information and exit.\n\n"));
90                 printf(_("Report bugs to tinc@tinc-vpn.org.\n"));
91         }
92 }
93
94 static bool parse_options(int argc, char **argv) {
95         int result;
96         int option_index = 0;
97
98         while((result = getopt_long(argc, argv, "c:DLd::k::n:", long_options, &option_index)) != EOF) {
99                 switch (result) {
100                         case 0:
101                                 break;
102
103                         case 'c': /* --config */
104                                 confbase = xstrdup(optarg);
105                                 break;
106
107                         case 'D': /* --no-detach */
108                                 do_detach = false;
109                                 break;
110
111                         case 'L': /* --mlock */
112                                 do_mlock = true;
113                                 break;
114
115                         case 'd': /* --debug */
116                                 if(optarg)
117                                         debug_level = atoi(optarg);
118                                 else
119                                         debug_level++;
120                                 break;
121
122                         case 'k': /* --kill */
123 #ifndef HAVE_MINGW
124                                 if(optarg) {
125                                         if(!strcasecmp(optarg, "HUP"))
126                                                 kill_tincd = SIGHUP;
127                                         else if(!strcasecmp(optarg, "TERM"))
128                                                 kill_tincd = SIGTERM;
129                                         else if(!strcasecmp(optarg, "KILL"))
130                                                 kill_tincd = SIGKILL;
131                                         else if(!strcasecmp(optarg, "USR1"))
132                                                 kill_tincd = SIGUSR1;
133                                         else if(!strcasecmp(optarg, "USR2"))
134                                                 kill_tincd = SIGUSR2;
135                                         else if(!strcasecmp(optarg, "WINCH"))
136                                                 kill_tincd = SIGWINCH;
137                                         else if(!strcasecmp(optarg, "INT"))
138                                                 kill_tincd = SIGINT;
139                                         else if(!strcasecmp(optarg, "ALRM"))
140                                                 kill_tincd = SIGALRM;
141                                         else {
142                                                 kill_tincd = atoi(optarg);
143
144                                                 if(!kill_tincd) {
145                                                         fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"),
146                                                                         optarg);
147                                                         usage(true);
148                                                         return false;
149                                                 }
150                                         }
151                                 } else
152                                         kill_tincd = SIGTERM;
153 #else
154                                         kill_tincd = 1;
155 #endif
156                                 break;
157
158                         case 'n': /* --net */
159                                 netname = xstrdup(optarg);
160                                 break;
161
162                         case 1: /* --help */
163                                 show_help = true;
164                                 break;
165
166                         case 2: /* --version */
167                                 show_version = true;
168                                 break;
169
170                         case 3: /* --bypass-security */
171                                 bypass_security = true;
172                                 break;
173
174                         case 4: /* --logfile */
175                                 use_logfile = true;
176                                 if(optarg)
177                                         logfilename = xstrdup(optarg);
178                                 break;
179
180                         case 5: /* --pidfile */
181                                 pidfilename = xstrdup(optarg);
182                                 break;
183
184                         case '?':
185                                 usage(true);
186                                 return false;
187
188                         default:
189                                 break;
190                 }
191         }
192
193         return true;
194 }
195
196 static void make_names(void)
197 {
198 #ifdef HAVE_MINGW
199         HKEY key;
200         char installdir[1024] = "";
201         long len = sizeof(installdir);
202 #endif
203
204         if(netname)
205                 asprintf(&identname, "tinc.%s", netname);
206         else
207                 identname = xstrdup("tinc");
208
209 #ifdef HAVE_MINGW
210         if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
211                 if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
212                         if(!logfilename)
213                                 asprintf(&logfilename, "%s/log/%s.log", identname);
214                         if(!confbase) {
215                                 if(netname)
216                                         asprintf(&confbase, "%s/%s", installdir, netname);
217                                 else
218                                         asprintf(&confbase, "%s", installdir);
219                         }
220                 }
221                 RegCloseKey(key);
222                 if(*installdir)
223                         return;
224         }
225 #endif
226
227         if(!pidfilename)
228                 asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
229
230         if(!logfilename)
231                 asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
232
233         if(!confbase) {
234                 if(netname)
235                         asprintf(&confbase, CONFDIR "/tinc/%s", netname);
236                 else
237                         asprintf(&confbase, CONFDIR "/tinc");
238         }
239
240         asprintf(&cfgfilename, "%s/tinc.conf", confbase);
241 }
242
243 int main(int argc, char **argv) {
244         tinc_argc = argc;
245         tinc_argv = argv;
246
247         setlocale(LC_ALL, "");
248         bindtextdomain(PACKAGE, LOCALEDIR);
249         textdomain(PACKAGE);
250
251         if(!parse_options(argc, argv))
252                 return 1;
253         
254         make_names();
255
256         if(show_version) {
257                 printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE,
258                            VERSION, __DATE__, __TIME__, PROT_CURRENT);
259                 printf(_("Copyright (C) 1998-2004 Ivo Timmermans, Guus Sliepen and others.\n"
260                                 "See the AUTHORS file for a complete list.\n\n"
261                                 "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
262                                 "and you are welcome to redistribute it under certain conditions;\n"
263                                 "see the file COPYING for details.\n"));
264
265                 return 0;
266         }
267
268         if(show_help) {
269                 usage(false);
270                 return 0;
271         }
272
273         if(kill_tincd)
274                 return !kill_other(kill_tincd);
275
276         openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
277
278         /* Lock all pages into memory if requested */
279
280         if(do_mlock)
281 #ifdef HAVE_MLOCKALL
282                 if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
283                         logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
284                                    strerror(errno));
285 #else
286         {
287                 logger(LOG_ERR, _("mlockall() not supported on this platform!"));
288 #endif
289                 return -1;
290         }
291
292         tinc_cfg = cfg_tree_new();
293
294         asprintf(cfgfilename, "%s/tinc.conf", confbase);
295         
296         if(!cfg_read_file(tinc_cfg, cfgfilename))
297                 return 1;
298
299 #ifdef HAVE_MINGW
300         if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
301                 logger(LOG_ERR, _("System call `%s' failed: %s"), "WSAStartup", winerror(GetLastError()));
302                 return 1;
303         }
304 #endif
305
306         if(do_detach && !detach())
307                 return 1;
308
309         if(!fd_init() || !tnl_init() || !rt_init())
310                 return 1;
311
312         fd_run();
313
314         rt_exit();
315         tnl_exit();
316         fd_exit();
317 end:
318         logger(LOG_NOTICE, _("Terminating"));
319
320 #ifndef HAVE_MINGW
321         remove_pid(pidfilename);
322 #endif
323
324         logger_exit();
325         
326         return 0;
327 }