- updated man pages
[opensuse:smpppd.git] / smpppd / smpppd.cc
1
2
3 /*
4  *  Author: Arvin Schnell <arvin@suse.de>
5  */
6
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <getopt.h>
12 #include <signal.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/utsname.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <fcntl.h>
25
26 #include "../config.h"
27 #include "connection-config.h"
28 #include "connection.h"
29 #include "config.h"
30 #include "frontend.h"
31 #include "backend.h"
32 #include "utils.h"
33 #include "defines.h"
34 #include "log.h"
35
36
37 const char* program_name;
38
39 bool detach = true;
40
41 int sockfd1 = -1;               // local frontend socket
42 int sockfd2 = -1;               // inet frontend socket
43
44 Ifcfgs ifcfgs;
45 Frontends frontends;
46
47 MyLog mylog;
48
49 bool reload_config = false;
50 bool reload_sysconfig = false;
51
52
53 void
54 startup ()
55 {
56     InterfaceList il;
57     il.load ();
58
59     for (unsigned int i = 0; i < il.size (); i++)
60     {
61         fprintf (stderr, "%d: %s\n", i, il[i]->filename.c_str ());
62         Ifcfg* tmp = new Ifcfg (il[i]->filename);
63         tmp->ifcfg_menu = il[i]->menuname;
64         tmp->providerlist.load (il[i]->mode);
65         tmp->log.set_max_lines (config.number_of_loglines);
66
67         string error_message;
68         tmp->config = ConnectionConfig::create (tmp->ifcfg,
69                                                 il[i]->dfl_provider_filename,
70                                                 &error_message);
71         if (!tmp->config) {
72             tmp->log.push_back (error_message);
73             continue;
74         }
75
76         ifcfgs.push_back (tmp);
77     }
78
79     system ("/usr/bin/killall -q -USR1 " _SMPPPD_IFCFG);
80 }
81
82
83 void
84 sigterm (int sig)
85 {
86     dprintf ("%s\n", __PRETTY_FUNCTION__);
87
88     // control_stop ();
89
90     if (sockfd1 > -1) close (sockfd1);
91     if (sockfd2 > -1) close (sockfd2);
92
93     /* Remove old local frontend socket. */
94     if (access (FRONTEND_SOCKET, F_OK) == 0)
95         unlink (FRONTEND_SOCKET);
96
97     syslogf (LOG_INFO, "terminating on signal %d", sig);
98     closelog ();
99
100     exit (EXIT_SUCCESS);
101 }
102
103
104 void
105 sighup (int sig)
106 {
107     dprintf ("%s\n", __PRETTY_FUNCTION__);
108
109     reload_config = true;
110     reload_sysconfig = true;
111
112     signal (sig, sighup);
113 }
114
115
116 void
117 sigchild (int sig)
118 {
119     dprintf ("%s\n", __PRETTY_FUNCTION__);
120
121     // processes.sigterm ();
122
123     signal (sig, sigchild);
124 }
125
126
127 bool
128 allowed_host (struct sockaddr_in* clientname)
129 {
130     u_int32_t ip = ntohl ((&clientname->sin_addr)->s_addr);
131
132     for (int i = 0; i < 4; i++) {
133         const int s = i << 3;
134         if ((((ip >> s) & 0xff) >= ((config.ip_min >> s) & 0xff)) &&
135             (((ip >> s) & 0xff) <= ((config.ip_max >> s) & 0xff)))
136             return true;
137     }
138
139     return false;
140 }
141
142
143 void
144 overview ()
145 {
146     printf ("overview:\n");
147
148     for (Ifcfgs::const_iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
149     {
150         printf ("%s:  ", (*it)->ifcfg.c_str ());
151
152         ConnectionConfig* c = (*it)->config;
153         if (!c)
154             printf ("no config");
155         else
156             printf ("%s  ", c->get_provider_filename ().c_str ());
157
158         Backend* b = (*it)->backend;
159         if (!b)
160             printf ("no backend");
161         else
162             printf ("%d  %lld %lld", b->get_status (), b->get_rx_bytes (),
163                     b->get_tx_bytes ());
164
165         printf ("\n");
166     }
167 }
168
169
170 void
171 loop ()
172 {
173     // note: I rely on the fact that on linux the timeout is modified to
174     // reflect the amount of time not slept
175     struct timeval timeout;
176     timeout.tv_sec = 1;
177     timeout.tv_usec = 0;
178
179     overview ();
180
181     while (true)
182     {
183         if (reload_config) {
184             reload_config = false;
185             config.load ();
186         }
187
188         /*
189         if (reload_sysconfig && (status == ERROR || status == DISCONNECTED)) {
190             mylog.sprintf (true, "Reloading interfaces and providers.");
191             reload_sysconfig = false;
192             control_select_config ();
193         }
194         */
195
196         fd_set read_fd_set, write_fd_set;
197
198         FD_ZERO (&read_fd_set);
199         FD_ZERO (&write_fd_set);
200
201         if (sockfd1 > -1) FD_SET (sockfd1, &read_fd_set);
202         if (sockfd2 > -1) FD_SET (sockfd2, &read_fd_set);
203
204         ifcfgs.prepare_select (&read_fd_set, &write_fd_set);
205         frontends.prepare_select (&read_fd_set, &write_fd_set);
206
207         int ret;
208
209         do {
210             ret = select (FD_SETSIZE, &read_fd_set, &write_fd_set, 0, &timeout);
211         } while (ret == -1 && errno == EINTR);
212
213         if (ret < 0) {
214             perror ("select");
215             exit (EXIT_FAILURE);
216         }
217
218         if (ret == 0) {
219             dprintf ("timeout\n");
220             timeout.tv_sec = 0;
221             timeout.tv_usec = 750000;
222             //if (connection)
223             //  connection->timer ();
224             overview ();
225             continue;
226         }
227
228         /* Connection request on original sockets. */
229
230         for (Ifcfgs::iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
231         {
232             Ifcfg* tmp1 = (*it);
233
234             if (tmp1->sockfd > -1 && FD_ISSET (tmp1->sockfd, &read_fd_set))
235             {
236                 struct sockaddr_in clientname;
237                 socklen_t size = sizeof (sockaddr_in);
238                 int status = accept (tmp1->sockfd, (struct sockaddr*) &clientname, &size);
239                 if (status < 0) {
240                     perror ("accept"); // FIXME
241                     exit (EXIT_FAILURE);
242                 }
243
244                 fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
245
246                 if (config.debug)
247                     syslog (LOG_INFO, "connected on local backend socket");
248
249                 dprintf ("connected on local backend socket %s\n",
250                          tmp1->ifcfg.c_str ());
251
252                 Backend* tmp2 = new Backend (status); // FIXME
253                 tmp1->backend = tmp2;
254                 tmp2->write_line ("hello");
255             }
256         }
257
258         if (sockfd1 > -1 && FD_ISSET (sockfd1, &read_fd_set))
259         {
260             struct sockaddr_in clientname;
261             socklen_t size = sizeof (sockaddr_in);
262             int status = accept (sockfd1, (struct sockaddr*) &clientname, &size);
263             if (status < 0) {
264                 perror ("accept"); // FIXME
265                 exit (EXIT_FAILURE);
266             }
267
268             fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
269
270             if (config.debug)
271                 syslog (LOG_INFO, "connected on local frontend socket");
272
273             dprintf ("connected on local frontend socket\n");
274
275             Frontend* tmp = new Frontend (status, clientname);
276             frontends.push_back (tmp);
277
278             tmp->authenticated = true;
279             tmp->write_line ("SuSE Meta pppd (smpppd), Version " VERSION);
280         }
281
282         if (sockfd2 > -1 && FD_ISSET (sockfd2, &read_fd_set))
283         {
284             struct sockaddr_in clientname;
285             socklen_t size = sizeof (sockaddr_in);
286             int status = accept (sockfd2, (struct sockaddr*) &clientname, &size);
287             if (status < 0) {
288                 perror ("accept"); // FIXME
289                 exit (EXIT_FAILURE);
290             }
291
292             fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
293
294             if (!allowed_host (&clientname))
295             {
296                 close (status);
297
298                 syslogf (LOG_INFO, "refused connection from host %s",
299                          inet_ntoa (clientname.sin_addr));
300
301                 dprintf ("refused connection from host %s\n",
302                          inet_ntoa (clientname.sin_addr));
303
304             } else {
305
306                 syslogf (LOG_INFO, "connected from host %s",
307                          inet_ntoa (clientname.sin_addr));
308
309                 dprintf ("connected from host %s\n",
310                          inet_ntoa (clientname.sin_addr));
311
312                 Frontend* tmp = new Frontend (status, clientname);
313                 frontends.push_back (tmp);
314
315                 if (config.password.empty ()) {
316                     tmp->authenticated = true;
317                     tmp->write_line ("SuSE Meta pppd (smpppd), Version " VERSION);
318                 } else {
319                     tmp->auth.make_challenge ();
320                     tmp->authenticated = false;
321                     tmp->write_line ("challenge = %s",
322                                      tmp->auth.get_challenge ().c_str ());
323                 }
324             }
325         }
326
327         /* Handle select for all backends and frontends. */
328
329         ifcfgs.handle_select (&read_fd_set, &write_fd_set);
330         frontends.handle_select (&read_fd_set, &write_fd_set);
331
332         /* Handle input of backends. */
333
334         for (Ifcfgs::const_iterator it = ifcfgs.begin ();
335              it != ifcfgs.end (); it++) {
336             Backend* b = (*it)->backend;
337             if (b)
338             {
339                 string tmp;
340                 while (b->read_line (tmp)) {
341                     switch (b->append_buffer (tmp))
342                     {
343                         case Backend::NEW_CONFIG: {
344
345                             ConnectionConfig* c = (*it)->config;
346
347                             assert ((*it)->ifcfg == b->get_ifcfg_filename ());
348
349                             if (c->get_provider_filename () != b->get_provider_filename ())
350                                 (*it)->select_provider (b->get_provider_filename ());
351
352                             overview ();
353
354                         } break;
355
356                         case Backend::NEW_STATUS:
357                             overview ();
358                             break;
359
360                         case Backend::NEW_RXTXBYTES:
361                             overview ();
362                             break;
363
364                         case Backend::NEW_LOG1:
365                         case Backend::NEW_LOG2:
366                             vector <string> tmp = b->get_last_log ();
367                             (*it)->log.insert ((*it)->log.end (), tmp.begin (), tmp.end ());
368                             break;
369                     }
370                 }
371             }
372         }
373
374         /* Delete and remove closed backends. */
375
376         for (Ifcfgs::iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
377         {
378             Backend* b = (*it)->backend;
379             if (b) {
380                 if (b->has_been_closed) {
381                     dprintf ("closing backend due to %s\n", b->error_occurred ?
382                              "error" : "request");
383                     delete (*it)->backend;
384                     (*it)->backend = 0;
385                 }
386             }
387         }
388
389         /* Handle input of frontends. */
390
391         for (Frontends::const_iterator it = frontends.begin ();
392              it != frontends.end (); it++) {
393             string tmp;
394             while (((*it)->read_line (tmp)))
395                 (*it)->handle_command (tmp.c_str ());
396         }
397
398         /* Delete and remove closed frontends. */
399
400         for (Frontends::iterator it = frontends.begin (); it != frontends.end (); )
401         {
402             if ((*it)->has_been_closed)
403             {
404                 dprintf ("closing frontend due to %s\n", (*it)->error_occurred ?
405                          "error" : "request");
406
407                 /* stop the connection if desired. */
408                 /*
409                 if (!(*it)->survive_me && frontends.size () == 1)
410                     if (config.stop_on_disconnect == Config::STOP_ALWAYS ||
411                         (config.stop_on_disconnect == Config::STOP_NOTDEMAND
412                          && (!connection || !connection->is_demand ())))
413                     {
414                         mylog.sprintf (false, "All frontends disconnected.");
415                         control_stop ();
416                     }
417                 */
418
419                 delete (*it);
420                 frontends.erase (it);
421             }
422             else
423                 it++;
424         }
425     }
426 }
427
428
429 void usage () __attribute__ ((__noreturn__));
430
431 void
432 usage ()
433 {
434     fprintf (stderr, "%s: Wrong or missing option.\nTry `%s --help' for more "
435              "information.\n", program_name, program_name);
436     exit (EXIT_FAILURE);
437 }
438
439
440 void help () __attribute__ ((__noreturn__));
441
442 void
443 help ()
444 {
445     printf ("SuSE's `smpppd' controls several internet connections (with the "
446             "help\nof `smpppd-ifcfg'). It is not intended for direct use. Use "
447             "`rcsmpppd'\ninstead.\n"
448             "\n");
449
450     printf ("      --detach     detach (default)\n"
451             "      --nodetach   nodetach\n"
452             "\n"
453             "  -h, --help       show this help\n"
454             "\n");
455
456     printf ("See `man smpppd' for even more information.\n");
457
458     exit (EXIT_SUCCESS);
459 }
460
461
462 void
463 handle_args (int argc, char* argv[])
464 {
465     struct option long_options[] = {
466         { "detach", 0, 0, 130 },
467         { "nodetach", 0, 0, 131 },
468         { "help", 0, 0, 'h' },
469         { 0, 0, 0, 0 }
470     };
471
472     while (true)
473     {
474         int option_index = 0;
475
476         int c = getopt_long (argc, argv, "h", long_options, &option_index);
477         if (c == EOF)
478             break;
479
480         switch (c)
481         {
482             case 130:
483                 detach = true;
484                 break;
485
486             case 131:
487                 detach = false;
488                 break;
489
490             case 'h':
491                 help ();
492
493             default:
494                 usage ();
495         }
496     }
497
498     if (optind != argc)
499         usage ();
500 }
501
502
503 int
504 main (int argc, char* argv[])
505 {
506     program_name = argv[0];
507
508     /* handle command line arguments. */
509     handle_args (argc, argv);
510
511     /* load configuations. */
512     config.load ();
513
514     /* install signal handlers. */
515     signal (SIGINT, &sigterm);
516     signal (SIGTERM, &sigterm);
517     signal (SIGHUP, &sighup);
518     signal (SIGCHLD, &sigchild);
519
520     /* open syslog. */
521     openlog ("smpppd", LOG_PID, LOG_DAEMON);
522     syslog (LOG_INFO, "smpppd version " VERSION " started");
523
524     /* create the sockets in the local namespace. */
525     sockfd1 = make_local_socket (FRONTEND_SOCKET);
526
527     /* create the socket in the internet namespace. */
528     if (config.open_inet_socket)
529         sockfd2 = make_inet_socket (config.port, config.ip_bind);
530
531     /* detach */
532     if (detach)
533         daemon (0, 0);
534
535     /* print version and server in log. */
536     struct utsname utsn;
537     uname (&utsn);
538     mylog.addf (true, "SuSE Meta pppd (smpppd), Version " VERSION
539                 " on %s.%s", utsn.nodename, utsn.domainname);
540
541     /* */
542     startup ();
543
544     /* loop forever. */
545     loop ();
546 }