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