- added options to accounting to change format of output
[opensuse:smpppd.git] / smpppd / cinternet.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 <string.h>
12 #include <getopt.h>
13 #include <signal.h>
14 #include <locale.h>
15
16 #include <string>
17 #include <list>
18 #include <queue>
19
20 using std::string;
21 using std::list;
22
23 #include "../config.h"
24 #include "server.h"
25 #include "utils.h"
26 #include "parse.h"
27 #include "getpass.h"
28
29
30 const char* program_name;
31
32
33 struct Janis
34 {
35     Janis (bool script, QAP_STYLE qap_style)
36         : script (script), qap_style (qap_style) {
37     }
38
39     const bool script;
40     const QAP_STYLE qap_style;
41 };
42
43
44 class Cmd
45 {
46
47 public:
48
49     enum Type { START, DIALIN, HANGUP, STOP, CHANNELS, STATUS, CONFIG, LOG,
50                 MAINLOG, IFCFGS, IFCFG_NAME, IFCFG_NUMBER, PROVIDERS,
51                 PROVIDER_NAME, PROVIDER_NUMBER, DEBUG };
52
53     Cmd (Type type) : type (type) { }
54     Cmd (Type type, string arg) : type (type), arg (arg) { }
55
56     bool execute (Server& server, const Janis& janis);
57
58 private:
59
60     Type type;
61     string arg;
62
63 };
64
65 typedef std::queue <Cmd*> Cmds;
66
67 Cmds cmds;
68
69
70 bool
71 Cmd::execute (Server& server, const Janis& janis)
72 {
73     switch (type)
74     {
75         case START: {
76
77             if (!server.check_ifcfg ()) {
78                 fprintf (stderr, "error: command start requires the "
79                          "specification of an interface\n");
80                 return false;
81             }
82
83             list <string> status;
84             if (!server.list_status (&status)) {
85                 fprintf (stderr, "error: command start failed\n");
86                 return false;
87             }
88
89             bool ask_password = false;
90             for (list <string>::const_iterator it = status.begin ();
91                  it != status.end (); it++)
92             {
93                 vector <string> columns;
94                 if (parse_values (*it, &columns) < 2) {
95                     fprintf (stderr, "error: command start failed\n");
96                     return false;
97                 }
98                 if (columns[0] == "ask-password") {
99                     ask_password = columns[1] == "1";
100                     break;
101                 }
102             }
103
104             if (!ask_password)
105             {
106                 if (!server.start ()) {
107                     fprintf (stderr, "error: command start failed\n");
108                     return false;
109                 }
110             }
111             else
112             {
113                 printf ("Enter the password for the internet provider.\n");
114                 string tmp = my_getpass ("Password: ");
115                 if (tmp.empty ())
116                     return false;
117                 if (!server.start (tmp)) {
118                     fprintf (stderr, "error: command start failed\n");
119                     return false;
120                 }
121             }
122
123             return true;
124
125         } break;
126
127         case DIALIN: {
128
129             if (!server.check_ifcfg ()) {
130                 fprintf (stderr, "error: command dialin requires the "
131                          "specification of an interface\n");
132                 return false;
133             }
134
135             if (!server.dialin ()) {
136                 fprintf (stderr, "error: command dialin failed\n");
137                 return false;
138             }
139
140             return true;
141
142         } break;
143
144         case HANGUP: {
145
146             if (!server.check_ifcfg ()) {
147                 fprintf (stderr, "error: command hangup requires the "
148                          "specification of an interface\n");
149                 return false;
150             }
151
152             if (!server.hangup ()) {
153                 fprintf (stderr, "error: command hangup failed\n");
154                 return false;
155             }
156
157             return true;
158
159         } break;
160
161         case STOP: {
162
163             if (!server.check_ifcfg ()) {
164                 fprintf (stderr, "error: command stop requires the "
165                          "specification of an interface\n");
166                 return false;
167             }
168
169             if (!server.stop ()) {
170                 fprintf (stderr, "error: command stop failed\n");
171                 return false;
172             }
173
174             return true;
175
176         } break;
177
178         case CHANNELS: {
179
180             if (!server.check_ifcfg ()) {
181                 fprintf (stderr, "error: command channels requires the "
182                          "specification of an interface\n");
183                 return false;
184             }
185
186             if (!server.channels (atoi (arg.c_str ()))) {
187                 fprintf (stderr, "error: command channels failed\n");
188                 return false;
189             }
190
191             return true;
192
193         } break;
194
195         case STATUS: {
196
197             if (!server.check_ifcfg ()) {
198                 fprintf (stderr, "error: command status requires the "
199                          "specification of an interface\n");
200                 return false;
201             }
202
203             list <string> status;
204             if (!server.list_status (&status)) {
205                 fprintf (stderr, "error: command status failed\n");
206                 return false;
207             }
208
209             for (list <string>::const_iterator it = status.begin ();
210                  it != status.end (); it++)
211             {
212                 vector <string> columns;
213                 if (parse_values (*it, &columns) < 2) {
214                     fprintf (stderr, "error: command status failed\n");
215                     return false;
216                 }
217                 if (!janis.script)
218                     printf ("%s: %s\n", columns[0].c_str (), columns[1].c_str ());
219                 else
220                     printf ("%s %s\n", columns[0].c_str (),
221                             qap (columns[1], janis.qap_style).c_str ());
222             }
223             return true;
224
225         } break;
226
227         case CONFIG: {
228
229             if (!server.check_ifcfg ()) {
230                 fprintf (stderr, "error: command config requires the "
231                          "specification of an interface\n");
232                 return false;
233             }
234
235             list <string> config;
236             if (!server.list_config (&config)) {
237                 fprintf (stderr, "error: command config failed\n");
238                 return false;
239             }
240
241             for (list <string>::const_iterator it = config.begin ();
242                  it != config.end (); it++)
243             {
244                 vector <string> columns;
245                 if (parse_values (*it, &columns) < 2) {
246                     fprintf (stderr, "error: command config failed\n");
247                     return false;
248                 }
249                 if (!janis.script)
250                     printf ("%s: %s\n", columns[0].c_str (), columns[1].c_str ());
251                 else
252                     printf ("%s %s\n", columns[0].c_str (),
253                             qap (columns[1], janis.qap_style).c_str ());
254             }
255             return true;
256
257         } break;
258
259         case LOG: {
260
261             if (!server.check_ifcfg ()) {
262                 fprintf (stderr, "error: command log requires the "
263                          "specification of an interface\n");
264                 return false;
265             }
266
267             list <string> log;
268             if (!server.list_log (&log)) {
269                 fprintf (stderr, "error: command log failed\n");
270                 return false;
271             }
272
273             for (list <string>::const_iterator it = log.begin ();
274                  it != log.end (); it++)
275             {
276                 vector <string> columns;
277                 if (parse_values (*it, &columns) < 2) {
278                     fprintf (stderr, "error: command log failed\n");
279                     return false;
280                 }
281                 if (!janis.script)
282                     printf ("%s\n", columns[1].c_str ());
283                 else
284                     printf ("%s %s\n", columns[0].c_str (),
285                             qap (columns[1], janis.qap_style).c_str ());
286             }
287             return true;
288
289         } break;
290
291         case MAINLOG: {
292
293             list <string> log;
294             if (!server.list_mainlog (&log)) {
295                 fprintf (stderr, "error: command mainlog failed\n");
296                 return false;
297             }
298
299             for (list <string>::const_iterator it = log.begin ();
300                  it != log.end (); it++)
301             {
302                 vector <string> columns;
303                 if (parse_values (*it, &columns) < 2) {
304                     fprintf (stderr, "error: command mainlog failed\n");
305                     return false;
306                 }
307                 if (!janis.script)
308                     printf ("%s\n", columns[1].c_str ());
309                 else
310                     printf ("%s %s\n", columns[0].c_str (),
311                             qap (columns[1], janis.qap_style).c_str ());
312             }
313             return true;
314
315         } break;
316
317         case IFCFGS: {
318
319             list <string> ifcfgs;
320             if (!server.list_ifcfgs (&ifcfgs)) {
321                 fprintf (stderr, "error: command interface-list failed\n");
322                 return false;
323             }
324
325             int i = 1;
326             for (list <string>::const_iterator it = ifcfgs.begin ();
327                  it != ifcfgs.end (); it++)
328             {
329                 vector <string> columns;
330                 if (parse_values (*it, &columns) < 2) {
331                     fprintf (stderr, "error: command interface-list failed\n");
332                     return false;
333                 }
334                 if (!janis.script)
335                     printf ("%02d %s %s\n", i++, columns[0].c_str (),
336                             columns[2].c_str ());
337                 else
338                     printf ("%d %s %s\n", i++, columns[0].c_str (),
339                             qap (columns[2], janis.qap_style).c_str ());
340             }
341             return true;
342
343         } break;
344
345         case IFCFG_NAME: {
346
347             if (!server.select_ifcfg (arg)) {
348                 fprintf (stderr, "error: command interface-name failed\n");
349                 return false;
350             }
351
352             return true;
353
354         } break;
355
356         case IFCFG_NUMBER: {
357
358             if (!server.select_ifcfg (atoi (arg.c_str ()))) {
359                 fprintf (stderr, "error: command interface-number failed\n");
360                 return false;
361             }
362
363             return true;
364
365         } break;
366
367         case PROVIDERS: {
368
369             if (!server.check_ifcfg ()) {
370                 fprintf (stderr, "error: command provider-list requires the "
371                          "specification of an interface\n");
372                 return false;
373             }
374
375             list <string> providers;
376             if (!server.list_providers (&providers)) {
377                 fprintf (stderr, "error: command provider-list failed\n");
378                 return false;
379             }
380
381             int i = 1;
382             for (list <string>::const_iterator it = providers.begin ();
383                  it != providers.end (); it++)
384             {
385                 vector <string> columns;
386                 if (parse_values (*it, &columns) < 3) {
387                     fprintf (stderr, "error: command provider-list failed\n");
388                     return false;
389                 }
390                 if (!janis.script)
391                     printf ("%02d %s %s\n", i++, columns[0].c_str (),
392                             columns[2].c_str ());
393                 else
394                     printf ("%d %s %s\n", i++, columns[0].c_str (),
395                             qap (columns[2], janis.qap_style).c_str ());
396             }
397             return true;
398
399         } break;
400
401         case PROVIDER_NAME: {
402
403             if (!server.check_ifcfg ()) {
404                 fprintf (stderr, "error: command provider-name requires the "
405                          "specification of an interface\n");
406                 return false;
407             }
408
409             if (!server.select_provider (arg)) {
410                 fprintf (stderr, "error: command provider-name failed\n");
411                 return false;
412             }
413
414             return true;
415
416         } break;
417
418         case PROVIDER_NUMBER: {
419
420             if (!server.check_ifcfg ()) {
421                 fprintf (stderr, "error: command provider-number requires the "
422                          "specification of an interface\n");
423                 return false;
424             }
425
426             if (!server.select_provider (atoi (arg.c_str ()))) {
427                 fprintf (stderr, "error: command provider-number failed\n");
428                 return false;
429             }
430
431             return true;
432
433         } break;
434
435         case DEBUG: {
436
437             if (!server.debug (arg == "on")) {
438                 fprintf (stderr, "error: command debug failed\n");
439                 return false;
440             }
441
442             return true;
443
444         } break;
445
446     }
447
448     return false;
449 }
450
451
452 bool
453 doit (bool verbose, bool only_local, const Janis& janis)
454 {
455     AutoServer autoserver (verbose);
456
457     if (!autoserver.connect (only_local)) {
458         if (verbose)
459             fprintf (stderr, "error: connection to smpppd failed\n");
460         else
461             fprintf (stderr, "error: connection to smpppd failed (use "
462                      "--verbose for more information)\n");
463         return false;
464     }
465
466     Server* server = autoserver.get_server ();
467
468     while (!cmds.empty ()) {
469         if (!cmds.front ()->execute (*server, janis))
470             return false;
471         cmds.pop ();
472     }
473
474     return true;
475 }
476
477
478 void usage () __attribute__ ((__noreturn__));
479
480 void
481 usage ()
482 {
483     fprintf (stderr, "%s: Wrong or missing option.\nTry `%s --help' for more "
484              "information.\n", program_name, program_name);
485     exit (EXIT_FAILURE);
486 }
487
488
489 void version () __attribute__ ((__noreturn__));
490
491 void
492 version ()
493 {
494     printf ("cinternet %s\n", VERSION);
495     exit (EXIT_SUCCESS);
496 }
497
498
499 void help () __attribute__ ((__noreturn__));
500
501 void
502 help ()
503 {
504     printf ("SuSE's `cinternet' is a frontend for `smpppd' and enables you "
505             "to\ncontrol your internet connections.\n"
506             "\n");
507
508     printf ("Mandatory or optional arguments to long options are also mandatory\n"
509             "or optional for any corresponding short options.\n"
510             "\n"
511             "  -I, --interface-list         list available interfaces\n"
512             "  -i, --interface-name=NAME    select a interface\n"
513             "      --interface-number=NUM   select a interface\n"
514             "\n"
515             "  -P, --provider-list          list available providers\n"
516             "  -p, --provider-name=NAME     select a provider\n"
517             "      --provider-number=NUM    select a provider\n"
518             "\n"
519             "  -A, --start                  start connection\n"
520             "  -D, --dialin                 trigger dialin (only for demand)\n"
521             "  -H, --hangup                 trigger hangup (only for demand)\n"
522             "  -O, --stop                   stop connection\n"
523             "  -C, --channels=NUM           control channel bundling\n"
524             "\n"
525             "  -s, --status                 show status\n"
526             "  -c, --config                 show configuration\n"
527             "  -l, --log                    show log\n"
528             "\n"
529             "  -d, --debug={on,off}         turn debugging on or off\n"
530             "\n"
531             "  -v, --verbose                be more verbose\n"
532             "  -o, --only-local             only connect local smpppd\n"
533             "\n"
534             "  -L, --mainlog                list main log of smpppd\n"
535             "\n"
536             "      --script[=STYLE]         format output for scripts\n"
537             "\n"
538             "      --version                show version number\n"
539             "      --help                   show this help\n"
540             "\n");
541
542     printf ("See `man cinternet' for even more information.\n");
543
544     exit (EXIT_SUCCESS);
545 }
546
547
548 int
549 main (int argc, char* argv[])
550 {
551     program_name = argv[0];
552
553     setlocale (LC_ALL, "");
554
555     struct option long_options[] = {
556
557         // does not require ifcfg
558         { "mainlog", 0, 0, 'L' },
559         { "interface-list", 0, 0, 'I' },
560
561         // requires ifcfg (or sets it)
562         { "interface-name", 1, 0, 'i' },
563         { "interface-number", 1, 0, 130 },
564         { "start", 0, 0, 'A' },
565         { "dialin", 0, 0, 'D' },
566         { "hangup", 0, 0, 'H' },
567         { "stop", 0, 0, 'O' },
568         { "channels", 1, 0, 'C' },
569         { "status", 0, 0, 's' },
570         { "config", 0, 0, 'c' },
571         { "log", 0, 0, 'l' },
572         { "provider-list", 0, 0, 'P' },
573         { "provider-name", 1, 0, 'p' },
574         { "provider-number", 1, 0, 131 },
575
576         // misc
577         { "debug", 1, 0, 'd' },
578         { "verbose", 0, 0, 'v' },
579         { "only-local", 0, 0, 'o' },
580         { "script", 2, 0, 140 },
581         { "version", 0, 0, 220 },
582         { "help", 0, 0, 221 },
583
584         { 0, 0, 0, 0 }
585     };
586
587     bool verbose = false;
588     bool only_local = false;
589     bool script = false;
590     QAP_STYLE qap_style = QAP_SH;
591
592     while (true)
593     {
594         int c = getopt_long (argc, argv, "LIi:ADHOsclPp:d:vo", long_options, 0);
595         if (c == EOF)
596             break;
597
598         switch (c)
599         {
600             case 'I':
601                 cmds.push (new Cmd (Cmd::IFCFGS));
602                 break;
603
604             case 'i':
605                 cmds.push (new Cmd (Cmd::IFCFG_NAME, optarg));
606                 break;
607
608             case 130:
609                 cmds.push (new Cmd (Cmd::IFCFG_NUMBER, optarg));
610                 break;
611
612             case 'L':
613                 cmds.push (new Cmd (Cmd::MAINLOG));
614                 break;
615
616             case 'A':
617                 cmds.push (new Cmd (Cmd::START));
618                 break;
619
620             case 'D':
621                 cmds.push (new Cmd (Cmd::DIALIN));
622                 break;
623
624             case 'H':
625                 cmds.push (new Cmd (Cmd::HANGUP));
626                 break;
627
628             case 'O':
629                 cmds.push (new Cmd (Cmd::STOP));
630                 break;
631
632             case 'C':
633                 cmds.push (new Cmd (Cmd::CHANNELS, optarg));
634                 break;
635
636             case 's':
637                 cmds.push (new Cmd (Cmd::STATUS));
638                 break;
639
640             case 'c':
641                 cmds.push (new Cmd (Cmd::CONFIG));
642                 break;
643
644             case 'l':
645                 cmds.push (new Cmd (Cmd::LOG));
646                 break;
647
648             case 'P':
649                 cmds.push (new Cmd (Cmd::PROVIDERS));
650                 break;
651
652             case 'p':
653                 cmds.push (new Cmd (Cmd::PROVIDER_NAME, optarg));
654                 break;
655
656             case 131:
657                 cmds.push (new Cmd (Cmd::PROVIDER_NUMBER, optarg));
658                 break;
659
660             case 'd':
661                 cmds.push (new Cmd (Cmd::DEBUG, optarg));
662                 break;
663
664             case 'v':
665                 verbose = true;
666                 break;
667
668             case 'o':
669                 only_local = true;
670                 break;
671
672             case 140:
673                 script = true;
674                 if (optarg && !set_qap_style (optarg, &qap_style)) {
675                     fprintf (stderr, "error: unknown script style\n");
676                     usage ();
677                 }
678                 break;
679
680             case 220:
681                 version ();
682
683             case 221:
684                 help ();
685
686             default:
687                 usage ();
688         }
689     }
690
691     if (optind != argc || cmds.empty ())
692         usage ();
693
694     if (!doit (verbose, only_local, Janis (script, qap_style)))
695         exit (EXIT_FAILURE);
696
697     exit (EXIT_SUCCESS);
698 }