* Moved to the new string tokenizer API
[citadel.git] / webcit / setup.c
1 /*
2  * $Id$
3  *
4  * WebCit setup utility
5  * 
6  * (This is basically just an install wizard.  It's not required.)
7  *
8  */
9
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/utsname.h>
19 #include <sys/wait.h>
20 #include <signal.h>
21 #include <netdb.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <pwd.h>
25
26 #include "webcit.h"
27
28 #ifdef HAVE_NEWT
29 #include <newt.h>
30 #endif
31
32
33 #define UI_TEXT         0       /* Default setup type -- text only */
34 #define UI_DIALOG       2       /* Use the 'dialog' program */
35 #define UI_SILENT       3       /* Silent running, for use in scripts */
36 #define UI_NEWT         4       /* Use the "newt" window library */
37
38 int setup_type;
39 char setup_directory[SIZ];
40 char init_entry[SIZ];
41 int using_web_installer = 0;
42 char suggested_url[SIZ];
43
44 /*
45  * Set an entry in inittab to the desired state
46  */
47 void set_init_entry(char *which_entry, char *new_state) {
48         char *inittab = NULL;
49         FILE *fp;
50         char buf[SIZ];
51         char entry[SIZ];
52         char levels[SIZ];
53         char state[SIZ];
54         char prog[SIZ];
55
56         inittab = strdup("");
57         if (inittab == NULL) return;
58
59         fp = fopen("/etc/inittab", "r");
60         if (fp == NULL) return;
61
62         while(fgets(buf, sizeof buf, fp) != NULL) {
63
64                 if (num_tokens(buf, ':') == 4) {
65                         extract_token(entry, buf, 0, ':', sizeof entry);
66                         extract_token(levels, buf, 1, ':', sizeof levels);
67                         extract_token(state, buf, 2, ':', sizeof state);
68                         extract_token(prog, buf, 3, ':', sizeof prog); /* includes 0x0a LF */
69
70                         if (!strcmp(entry, which_entry)) {
71                                 strcpy(state, new_state);
72                                 sprintf(buf, "%s:%s:%s:%s",
73                                         entry, levels, state, prog);
74                         }
75                 }
76
77                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
78                 if (inittab == NULL) {
79                         fclose(fp);
80                         return;
81                 }
82                 
83                 strcat(inittab, buf);
84         }
85         fclose(fp);
86         fp = fopen("/etc/inittab", "w");
87         if (fp != NULL) {
88                 fwrite(inittab, strlen(inittab), 1, fp);
89                 fclose(fp);
90                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
91         }
92         free(inittab);
93 }
94
95
96
97
98 /* 
99  * Shut down the Citadel service if necessary, during setup.
100  */
101 void shutdown_service(void) {
102         FILE *infp;
103         char buf[SIZ];
104         char looking_for[SIZ];
105         int have_entry = 0;
106         char entry[SIZ];
107         char prog[SIZ];
108
109         strcpy(init_entry, "");
110
111         /* Determine the fully qualified path name of webserver */
112         snprintf(looking_for, sizeof looking_for, "%s/webserver ", setup_directory);
113
114         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
115          * an entry is found which we believe starts webserver.
116          */
117         infp = fopen("/etc/inittab", "r");
118         if (infp == NULL) {
119                 return;
120         } else {
121                 while (fgets(buf, sizeof buf, infp) != NULL) {
122                         buf[strlen(buf) - 1] = 0;
123                         extract_token(entry, buf, 0, ':', sizeof entry);
124                         extract_token(prog, buf, 3, ':', sizeof prog);
125                         if (!strncasecmp(prog, looking_for,
126                            strlen(looking_for))) {
127                                 ++have_entry;
128                                 strcpy(init_entry, entry);
129                         }
130                 }
131                 fclose(infp);
132         }
133
134         /* Bail out if there's nothing to do. */
135         if (!have_entry) return;
136
137         set_init_entry(init_entry, "off");
138 }
139
140
141 /*
142  * Start the Citadel service.
143  */
144 void start_the_service(void) {
145         if (strlen(init_entry) > 0) {
146                 set_init_entry(init_entry, "respawn");
147         }
148 }
149
150
151
152 void cleanup(int exitcode)
153 {
154 #ifdef HAVE_NEWT
155         newtCls();
156         newtRefresh();
157         newtFinished();
158 #endif
159         exit(exitcode);
160 }
161
162
163
164 void title(char *text)
165 {
166         if (setup_type == UI_TEXT) {
167                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
168         }
169 }
170
171
172
173 int yesno(char *question)
174 {
175 #ifdef HAVE_NEWT
176         newtComponent form = NULL;
177         newtComponent yesbutton = NULL;
178         newtComponent nobutton = NULL;
179 #endif
180         int i = 0;
181         int answer = 0;
182         char buf[SIZ];
183
184         switch (setup_type) {
185
186         case UI_TEXT:
187                 do {
188                         printf("%s\nYes/No --> ", question);
189                         fgets(buf, sizeof buf, stdin);
190                         answer = tolower(buf[0]);
191                         if (answer == 'y')
192                                 answer = 1;
193                         else if (answer == 'n')
194                                 answer = 0;
195                 } while ((answer < 0) || (answer > 1));
196                 break;
197
198         case UI_DIALOG:
199                 sprintf(buf, "exec %s --yesno '%s' 10 72",
200                         getenv("CTDL_DIALOG"),
201                         question);
202                 i = system(buf);
203                 if (i == 0) {
204                         answer = 1;
205                 }
206                 else {
207                         answer = 0;
208                 }
209                 break;
210
211 #ifdef HAVE_NEWT
212         case UI_NEWT:
213                 newtCenteredWindow(76, 10, "Question");
214                 form = newtForm(NULL, NULL, 0);
215                 for (i=0; i<num_tokens(question, '\n'); ++i) {
216                         extract_token(buf, question, i, '\n', sizeof buf);
217                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
218                 }
219                 yesbutton = newtButton(10, 5, "Yes");
220                 nobutton = newtButton(60, 5, "No");
221                 newtFormAddComponent(form, yesbutton);
222                 newtFormAddComponent(form, nobutton);
223                 if (newtRunForm(form) == yesbutton) {
224                         answer = 1;
225                 }
226                 else {
227                         answer = 0;
228                 }
229                 newtPopWindow();
230                 newtFormDestroy(form);  
231
232                 break;
233 #endif
234
235         }
236         return (answer);
237 }
238
239 void set_value(char *prompt, char str[])
240 {
241 #ifdef HAVE_NEWT
242         newtComponent form;
243         char *result;
244         int i;
245 #endif
246         char buf[SIZ];
247         char *dialog_result;
248         char setupmsg[SIZ];
249         FILE *fp;
250
251         strcpy(setupmsg, "");
252
253         switch (setup_type) {
254         case UI_TEXT:
255                 title("WebCit setup");
256                 printf("\n%s\n", prompt);
257                 printf("This is currently set to:\n%s\n", str);
258                 printf("Enter new value or press return to leave unchanged:\n");
259                 fgets(buf, sizeof buf, stdin);
260                 buf[strlen(buf) - 1] = 0;
261                 if (strlen(buf) != 0)
262                         strcpy(str, buf);
263                 break;
264
265         case UI_DIALOG:
266                 dialog_result = tmpnam(NULL);
267                 sprintf(buf, "exec %s --backtitle '%s' --inputbox '%s' 19 72 '%s' 2>%s",
268                         getenv("CTDL_DIALOG"),
269                         "WebCit setup",
270                         prompt,
271                         str,
272                         dialog_result);
273                 system(buf);
274                 fp = fopen(dialog_result, "r");
275                 if (fp != NULL) {
276                         fgets(str, sizeof buf, fp);
277                         if (str[strlen(str)-1] == 10) {
278                                 str[strlen(str)-1] = 0;
279                         }
280                         fclose(fp);
281                         unlink(dialog_result);
282                 }
283                 break;
284
285 #ifdef HAVE_NEWT
286         case UI_NEWT:
287
288                 newtCenteredWindow(76, 10, "WebCit setup");
289                 form = newtForm(NULL, NULL, 0);
290                 for (i=0; i<num_tokens(prompt, '\n'); ++i) {
291                         extract_token(buf, prompt, i, '\n', sizeof buf);
292                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
293                 }
294                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
295                                         NEWT_FLAG_RETURNEXIT));
296                 newtRunForm(form);
297                 strcpy(str, result);
298
299                 newtPopWindow();
300                 newtFormDestroy(form);  
301
302 #endif
303         }
304 }
305
306
307 void important_message(char *title, char *msgtext)
308 {
309 #ifdef HAVE_NEWT
310         newtComponent form = NULL;
311         int i = 0;
312 #endif
313         char buf[SIZ];
314
315         switch (setup_type) {
316
317         case UI_TEXT:
318                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
319                 printf("       %s \n\n%s\n\n", title, msgtext);
320                 printf("Press return to continue...");
321                 fgets(buf, sizeof buf, stdin);
322                 break;
323
324         case UI_DIALOG:
325                 sprintf(buf, "exec %s --backtitle '%s' --msgbox '%s' 19 72",
326                         getenv("CTDL_DIALOG"),
327                         title,
328                         msgtext);
329                 system(buf);
330                 break;
331
332 #ifdef HAVE_NEWT
333         case UI_NEWT:
334                 newtCenteredWindow(76, 10, title);
335                 form = newtForm(NULL, NULL, 0);
336                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
337                         extract_token(buf, msgtext, i, '\n', sizeof buf);
338                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
339                 }
340                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
341                 newtRunForm(form);
342                 newtPopWindow();
343                 newtFormDestroy(form);  
344                 break;
345 #endif
346
347         }
348 }
349
350
351 void display_error(char *error_message)
352 {
353         important_message("Error", error_message);
354 }
355
356 void progress(char *text, long int curr, long int cmax)
357 {
358 #ifdef HAVE_NEWT
359
360         /* These variables are static because progress() gets called
361          * multiple times during the course of whatever operation is
362          * being performed.  This makes setup non-threadsafe, but who
363          * cares?
364          */
365         static newtComponent form = NULL;
366         static newtComponent scale = NULL;
367 #endif
368         static long dots_printed = 0L;
369         long a = 0;
370         char buf[SIZ];
371         static FILE *fp = NULL;
372
373         switch (setup_type) {
374
375         case UI_TEXT:
376                 if (curr == 0) {
377                         printf("%s\n", text);
378                         printf("..........................");
379                         printf("..........................");
380                         printf("..........................\r");
381                         fflush(stdout);
382                         dots_printed = 0;
383                 } else if (curr == cmax) {
384                         printf("\r%79s\n", "");
385                 } else {
386                         a = (curr * 100) / cmax;
387                         a = a * 78;
388                         a = a / 100;
389                         while (dots_printed < a) {
390                                 printf("*");
391                                 ++dots_printed;
392                                 fflush(stdout);
393                         }
394                 }
395                 break;
396
397         case UI_DIALOG:
398                 if (curr == 0) {
399                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
400                                 getenv("CTDL_DIALOG"),
401                                 text);
402                         fp = popen(buf, "w");
403                         if (fp != NULL) {
404                                 fprintf(fp, "0\n");
405                                 fflush(fp);
406                         }
407                 } 
408                 else if (curr == cmax) {
409                         if (fp != NULL) {
410                                 fprintf(fp, "100\n");
411                                 pclose(fp);
412                                 fp = NULL;
413                         }
414                 }
415                 else {
416                         a = (curr * 100) / cmax;
417                         if (fp != NULL) {
418                                 fprintf(fp, "%ld\n", a);
419                                 fflush(fp);
420                         }
421                 }
422                 break;
423
424 #ifdef HAVE_NEWT
425         case UI_NEWT:
426                 if (curr == 0) {
427                         newtCenteredWindow(76, 8, text);
428                         form = newtForm(NULL, NULL, 0);
429                         scale = newtScale(1, 3, 74, cmax);
430                         newtFormAddComponent(form, scale);
431                         newtDrawForm(form);
432                         newtRefresh();
433                 }
434                 if ((curr > 0) && (curr <= cmax)) {
435                         newtScaleSet(scale, curr);
436                         newtRefresh();
437                 }
438                 if (curr == cmax) {
439                         newtFormDestroy(form);  
440                         newtPopWindow();
441                         newtRefresh();
442                 }
443                 break;
444 #endif
445
446         }
447 }
448
449
450
451
452 /*
453  * check_inittab_entry()  -- Make sure "webserver" is in /etc/inittab
454  *
455  */
456 void check_inittab_entry(void)
457 {
458         FILE *infp;
459         char buf[SIZ];
460         char looking_for[SIZ];
461         char question[SIZ];
462         char entryname[5];
463         char http_port[128];
464 #ifdef HAVE_OPENSSL
465         char https_port[128];
466 #endif
467         char hostname[128];
468         char portname[128];
469         struct utsname my_utsname;
470
471         /* Determine the fully qualified path name of webserver */
472         snprintf(looking_for, sizeof looking_for, "%s/webserver", setup_directory);
473
474         /* If there's already an entry, then we have nothing left to do. */
475         if (strlen(init_entry) > 0) {
476                 return;
477         }
478
479         /* Otherwise, prompt the user to create an entry. */
480         snprintf(question, sizeof question,
481                 "There is no '%s' entry in /etc/inittab.\n"
482                 "Would you like to add one?",
483                 looking_for);
484         if (yesno(question) == 0)
485                 return;
486
487         snprintf(question, sizeof question,
488                 "On which port do you want WebCit to listen for HTTP "
489                 "requests?\n\nYou can use the standard port (80) if you are "
490                 "not running another\nweb server (such as Apache), otherwise "
491                 "select another port.");
492         sprintf(http_port, "2000");
493         set_value(question, http_port);
494         uname(&my_utsname);
495         sprintf(suggested_url, "http://%s:%s/", my_utsname.nodename, http_port);
496
497 #ifdef HAVE_OPENSSL
498         snprintf(question, sizeof question,
499                 "On which port do you want WebCit to listen for HTTPS "
500                 "requests?\n\nYou can use the standard port (443) if you are "
501                 "not running another\nweb server (such as Apache), otherwise "
502                 "select another port.");
503         sprintf(https_port, "443");
504         set_value(question, https_port);
505 #endif
506
507         /* Find out where Citadel is. */
508         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
509                 strcpy(hostname, "uds");
510                 strcpy(portname, getenv("CITADEL"));
511         }
512         else {
513                 snprintf(question, sizeof question,
514                         "Is the Citadel service running on the same host as WebCit?");
515                 if (yesno(question)) {
516                         sprintf(hostname, "uds");
517                         sprintf(portname, "/usr/local/citadel");
518                         set_value("In what directory is Citadel installed?", portname);
519                 }
520                 else {
521                         sprintf(hostname, "127.0.0.1");
522                         sprintf(portname, "504");
523                         set_value("Enter the host name or IP address of your "
524                                 "Citadel server.", hostname);
525                         set_value("Enter the port number on which Citadel is "
526                                 "running (usually 504)", portname);
527                 }
528         }
529
530         /* Generate unique entry names for /etc/inittab */
531         snprintf(entryname, sizeof entryname, "c0");
532         do {
533                 ++entryname[1];
534                 if (entryname[1] > '9') {
535                         entryname[1] = 0;
536                         ++entryname[0];
537                         if (entryname[0] > 'z') {
538                                 display_error(
539                                    "Can't generate a unique entry name");
540                                 return;
541                         }
542                 }
543                 snprintf(buf, sizeof buf,
544                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
545         } while (system(buf) == 0);
546         
547
548         /* Now write it out to /etc/inittab */
549         infp = fopen("/etc/inittab", "a");
550         if (infp == NULL) {
551                 display_error(strerror(errno));
552         } else {
553                 fprintf(infp, "# Start the WebCit server...\n");
554                 fprintf(infp, "h%s:2345:respawn:%s -p%s %s %s\n",
555                         entryname, looking_for,
556                         http_port, hostname, portname);
557 #ifdef HAVE_OPENSSL
558                 fprintf(infp, "s%s:2345:respawn:%s -p%s -s %s %s\n",
559                         entryname, looking_for,
560                         https_port, hostname, portname);
561 #endif
562                 fclose(infp);
563                 strcpy(init_entry, entryname);
564         }
565 }
566
567
568
569
570 /*
571  * Figure out what type of user interface we're going to use
572  */
573 int discover_ui(void)
574 {
575
576         /* Use "dialog" if we have it */
577         if (getenv("CTDL_DIALOG") != NULL) {
578                 return UI_DIALOG;
579         }
580                 
581 #ifdef HAVE_NEWT
582         newtInit();
583         newtCls();
584         newtDrawRootText(0, 0, "WebCit Setup");
585         return UI_NEWT;
586 #endif
587         return UI_TEXT;
588 }
589
590
591
592
593
594 int main(int argc, char *argv[])
595 {
596         int a;
597         char aaa[256];
598         int info_only = 0;
599         strcpy(suggested_url, "http://<your_host_name>:<port>/");
600
601         /* set an invalid setup type */
602         setup_type = (-1);
603
604         /* Check to see if we're running the web installer */
605         if (getenv("CITADEL_INSTALLER") != NULL) {
606                 using_web_installer = 1;
607         }
608
609         /* parse command line args */
610         for (a = 0; a < argc; ++a) {
611                 if (!strncmp(argv[a], "-u", 2)) {
612                         strcpy(aaa, argv[a]);
613                         strcpy(aaa, &aaa[2]);
614                         setup_type = atoi(aaa);
615                 }
616                 if (!strcmp(argv[a], "-i")) {
617                         info_only = 1;
618                 }
619                 if (!strcmp(argv[a], "-q")) {
620                         setup_type = UI_SILENT;
621                 }
622         }
623
624
625         /* If a setup type was not specified, try to determine automatically
626          * the best one to use out of all available types.
627          */
628         if (setup_type < 0) {
629                 setup_type = discover_ui();
630         }
631         if (info_only == 1) {
632                 important_message("WebCit Setup", "Welcome to WebCit setup");
633                 cleanup(0);
634         }
635
636         /* Get started in a valid setup directory. */
637         strcpy(setup_directory, WEBCITDIR);
638         if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) {
639                 strcpy(setup_directory, getenv("WEBCIT"));
640         }
641         else {
642                 set_value("In what directory is WebCit installed?",
643                         setup_directory);
644         }
645         if (chdir(setup_directory) != 0) {
646                 important_message("WebCit Setup",
647                           "The directory you specified does not exist.");
648                 cleanup(errno);
649         }
650
651         /* See if we need to shut down the WebCit service. */
652         for (a=0; a<=3; ++a) {
653                 progress("Shutting down the WebCit service...", a, 3);
654                 if (a == 0) shutdown_service();
655                 sleep(1);
656         }
657
658         /* Now begin. */
659         switch (setup_type) {
660
661         case UI_TEXT:
662                 printf("\n\n\n"
663                         "               *** WebCit setup program ***\n\n");
664                 break;
665
666         }
667
668         check_inittab_entry();  /* Check /etc/inittab */
669
670         /* See if we can start the WebCit service. */
671         if (strlen(init_entry) > 0) {
672                 for (a=0; a<=3; ++a) {
673                         progress("Starting the WebCit service...", a, 3);
674                         if (a == 0) start_the_service();
675                         sleep(1);
676                 }
677                 sprintf(aaa,
678                         "Setup is finished.  You may now log in.\n"
679                         "Point your web browser at %s\n", suggested_url);
680                 important_message("Setup finished", aaa);
681         }
682         else {
683                 important_message("Setup finished",
684                         "Setup is finished.  You may now start the server.");
685         }
686
687         cleanup(0);
688         return 0;
689 }