Move all process-related functions into process.c.
[oweals/tinc.git] / src / tincd.c
1 /*
2     tincd.c -- the main file for tincd
3     Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
4                             2000 Guus Sliepen <guus@sliepen.warande.net>
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: tincd.c,v 1.10.4.29 2000/11/16 17:54:29 zarq Exp $
21 */
22
23 #include "config.h"
24
25 #include <errno.h>
26 #include <fcntl.h> 
27 #include <getopt.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <termios.h>
36
37 #ifdef HAVE_SYS_IOCTL_H
38 # include <sys/ioctl.h>
39 #endif
40
41 #ifdef HAVE_OPENSSL_RAND_H
42 # include <openssl/rand.h>
43 #else
44 # include <rand.h>
45 #endif
46
47 #ifdef HAVE_OPENSSL_RSA_H
48 # include <openssl/rsa.h>
49 #else
50 # include <rsa.h>
51 #endif
52
53 #ifdef HAVE_OPENSSL_ERR_H
54 # include <openssl/err.h>
55 #else
56 # include <err.h>
57 #endif
58
59
60
61 #include <pidfile.h>
62 #include <utils.h>
63 #include <xalloc.h>
64
65 #include "conf.h"
66 #include "net.h"
67 #include "netutl.h"
68 #include "protocol.h"
69 #include "subnet.h"
70
71 #include "system.h"
72
73 /* The name this program was run with. */
74 char *program_name;
75
76 /* If nonzero, display usage information and exit. */
77 static int show_help;
78
79 /* If nonzero, print the version on standard output and exit.  */
80 static int show_version;
81
82 /* If nonzero, it will attempt to kill a running tincd and exit. */
83 static int kill_tincd = 0;
84
85 /* If zero, don't detach from the terminal. */
86 extern int do_detach;
87
88 /* If nonzero, generate public/private keypair for this host/net. */
89 static int generate_keys = 0;
90
91 char *identname;                 /* program name for syslog */
92 char *pidfilename;               /* pid file location */
93 static pid_t ppid;               /* pid of non-detached part */
94 char **g_argv;                   /* a copy of the cmdline arguments */
95 char **environment;              /* A pointer to the environment on
96                                     startup */
97
98 void cleanup_and_exit(int);
99 int detach(void);
100 int kill_other(void);
101 void make_names(void);
102 RETSIGTYPE parent_exit(int a);
103 void setup_signals(void);
104 int write_pidfile(void);
105
106 static struct option const long_options[] =
107 {
108   { "config", required_argument, NULL, 'c' },
109   { "kill", no_argument, NULL, 'k' },
110   { "net", required_argument, NULL, 'n' },
111   { "help", no_argument, &show_help, 1 },
112   { "version", no_argument, &show_version, 1 },
113   { "no-detach", no_argument, &do_detach, 0 },
114   { "generate-keys", optional_argument, NULL, 'K'},
115   { NULL, 0, NULL, 0 }
116 };
117
118 static void
119 usage(int status)
120 {
121   if(status != 0)
122     fprintf(stderr, _("Try `%s --help\' for more information.\n"), program_name);
123   else
124     {
125       printf(_("Usage: %s [option]...\n\n"), program_name);
126       printf(_("  -c, --config=DIR           Read configuration options from DIR.\n"
127                "  -D, --no-detach            Don't fork and detach.\n"
128                "  -d                         Increase debug level.\n"
129                "  -k, --kill                 Attempt to kill a running tincd and exit.\n"
130                "  -n, --net=NETNAME          Connect to net NETNAME.\n"));
131       printf(_("  -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
132                "      --help                 Display this help and exit.\n"
133                "      --version              Output version information and exit.\n\n"));
134       printf(_("Report bugs to tinc@nl.linux.org.\n"));
135     }
136   exit(status);
137 }
138
139 void
140 parse_options(int argc, char **argv, char **envp)
141 {
142   int r;
143   int option_index = 0;
144   
145   while((r = getopt_long(argc, argv, "c:Ddkn:K::", long_options, &option_index)) != EOF)
146     {
147       switch(r)
148         {
149         case 0: /* long option */
150           break;
151         case 'c': /* config file */
152           confbase = xmalloc(strlen(optarg)+1);
153           strcpy(confbase, optarg);
154           break;
155         case 'D': /* no detach */
156           do_detach = 0;
157           break;
158         case 'd': /* inc debug level */
159           debug_lvl++;
160           break;
161         case 'k': /* kill old tincds */
162           kill_tincd = 1;
163           break;
164         case 'n': /* net name given */
165           netname = xmalloc(strlen(optarg)+1);
166           strcpy(netname, optarg);
167           break;
168         case 'K': /* generate public/private keypair */
169           if(optarg)
170             {
171               generate_keys = atoi(optarg);
172               if(generate_keys < 512)
173                 {
174                   fprintf(stderr, _("Invalid argument! BITS must be a number equal to or greater than 512.\n"));
175                   usage(1);
176                 }
177               generate_keys &= ~7;      /* Round it to bytes */
178             }
179           else
180             generate_keys = 1024;
181           break;
182         case '?':
183           usage(1);
184         default:
185           break;
186         }
187     }
188 }
189
190 /* This function prettyprints the key generation process */
191
192 void indicator(int a, int b, void *p)
193 {
194   switch(a)
195   {
196     case 0:
197       fprintf(stderr, ".");
198       break;
199     case 1:
200       fprintf(stderr, "+");
201       break;
202     case 2:
203       fprintf(stderr, "-");
204       break;
205     case 3:
206       switch(b)
207         {
208           case 0:
209             fprintf(stderr, " p\n");      
210             break;
211           case 1:
212             fprintf(stderr, " q\n");
213             break;
214           default:
215             fprintf(stderr, "?");
216          }
217        break;
218     default:
219       fprintf(stderr, "?");
220   }
221 }
222
223 /* Generate a public/private RSA keypair, and possibly store it into the configuration file. */
224
225 int keygen(int bits)
226 {
227   RSA *rsa_key;
228
229   fprintf(stderr, _("Generating %d bits keys:\n"), bits);
230   rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
231   if(!rsa_key)
232     {
233       fprintf(stderr, _("Error during key generation!"));
234       return -1;
235      }
236   else
237     fprintf(stderr, _("Done.\n"));
238
239   fprintf(stderr, _("Please copy the private key to tinc.conf and the\npublic key to your host configuration file:\n\n"));
240   printf("PublicKey = %s\n", BN_bn2hex(rsa_key->n));
241   printf("PrivateKey = %s\n", BN_bn2hex(rsa_key->d));
242   
243   fflush(stdin);
244   return 0;
245 }
246
247 void memory_full(int size)
248 {
249   syslog(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exiting."), size);
250   cp_trace();
251   exit(1);
252 }
253
254 /*
255   Close network connections, and terminate neatly
256 */
257 void cleanup_and_exit(int c)
258 {
259   close_network_connections();
260
261   if(debug_lvl > DEBUG_NOTHING)
262     syslog(LOG_INFO, _("Total bytes written: tap %d, socket %d; bytes read: tap %d, socket %d"),
263            total_tap_out, total_socket_out, total_tap_in, total_socket_in);
264
265   closelog();
266   kill(ppid, SIGTERM);
267   exit(c);
268 }
269
270 /*
271   check for an existing tinc for this net, and write pid to pidfile
272 */
273 int write_pidfile(void)
274 {
275   int pid;
276
277   if((pid = check_pid(pidfilename)))
278     {
279       if(netname)
280         fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
281                 netname, pid);
282       else
283         fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
284       return 1;
285     }
286
287   /* if it's locked, write-protected, or whatever */
288   if(!write_pid(pidfilename))
289     return 1;
290
291   return 0;
292 }
293
294 /*
295   kill older tincd for this net
296 */
297 int kill_other(void)
298 {
299   int pid;
300
301   if(!(pid = read_pid(pidfilename)))
302     {
303       if(netname)
304         fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
305       else
306         fprintf(stderr, _("No other tincd is running.\n"));
307       return 1;
308     }
309
310   errno = 0;    /* No error, sometimes errno is only changed on error */
311   /* ESRCH is returned when no process with that pid is found */
312   if(kill(pid, SIGTERM) && errno == ESRCH)
313     fprintf(stderr, _("Removing stale lock file.\n"));
314   remove_pid(pidfilename);
315
316   return 0;
317 }
318
319 /*
320   Set all files and paths according to netname
321 */
322 void make_names(void)
323 {
324   if(netname)
325     {
326       if(!pidfilename)
327         asprintf(&pidfilename, "/var/run/tinc.%s.pid", netname);
328       if(!confbase)
329         asprintf(&confbase, "%s/tinc/%s", CONFDIR, netname);
330       else
331         fprintf(stderr, _("Both netname and configuration directory given, using the latter...\n"));
332       if(!identname)
333         asprintf(&identname, "tinc.%s", netname);
334     }
335   else
336     {
337       if(!pidfilename)
338         pidfilename = "/var/run/tinc.pid";
339       if(!confbase)
340         asprintf(&confbase, "%s/tinc", CONFDIR);
341       if(!identname)
342         identname = "tinc";
343     }
344 }
345
346 int
347 main(int argc, char **argv, char **envp)
348 {
349   program_name = argv[0];
350
351   setlocale (LC_ALL, "");
352   bindtextdomain (PACKAGE, LOCALEDIR);
353   textdomain (PACKAGE);
354
355   /* Do some intl stuff right now */
356   
357   unknown = _("unknown");
358
359   environment = envp;
360   parse_options(argc, argv, envp);
361
362   if(show_version)
363     {
364       printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT);
365       printf(_("Copyright (C) 1998,1999,2000 Ivo Timmermans, Guus Sliepen and others.\n"
366                "See the AUTHORS file for a complete list.\n\n"
367                "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
368                "and you are welcome to redistribute it under certain conditions;\n"
369                "see the file COPYING for details.\n"));
370
371       return 0;
372     }
373
374   if(show_help)
375     usage(0);
376
377   if(geteuid())
378     {
379       fprintf(stderr, _("You must be root to run this program. Sorry.\n"));
380       return 1;
381     }
382
383   g_argv = argv;
384
385   make_names();
386
387   /* Slllluuuuuuurrrrp! */
388
389   RAND_load_file("/dev/urandom", 1024);
390
391   if(generate_keys)
392     exit(keygen(generate_keys));
393
394   if(kill_tincd)
395     exit(kill_other());
396
397   if(read_server_config())
398     return 1;
399
400   setup_signals();
401
402   if(detach())
403     exit(0);
404
405   if(debug_lvl >= DEBUG_ERROR)
406     ERR_load_crypto_strings();
407     
408   for(;;)
409     {
410       if(!setup_network_connections())
411         {
412           main_loop();
413           cleanup_and_exit(1);
414         }
415       
416       syslog(LOG_ERR, _("Unrecoverable error"));
417       cp_trace();
418
419       if(do_detach)
420         {
421           syslog(LOG_NOTICE, _("Restarting in %d seconds!"), MAXTIMEOUT);
422           sleep(MAXTIMEOUT);
423         }
424       else
425         {
426           syslog(LOG_ERR, _("Not restarting."));
427           exit(0);
428         }
429     }
430 }
431