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