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