* call SetTitles()
[citadel.git] / citadel / utils / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel setup utility
5  *
6  */
7
8 #define SHOW_ME_VAPPEND_PRINTF
9
10 #include "ctdl_module.h"
11
12
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <fcntl.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/utsname.h>
22 #include <sys/wait.h>
23 #include <signal.h>
24 #include <netdb.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <pwd.h>
28 #include <time.h>
29 #include <libcitadel.h>
30 #include "citadel.h"
31 #include "axdefs.h"
32 #include "sysdep.h"
33 #include "config.h"
34 #include "citadel_dirs.h"
35 #if HAVE_BACKTRACE
36 #include <execinfo.h>
37 #endif
38
39 #ifdef ENABLE_NLS
40 #ifdef HAVE_XLOCALE_H
41 #include <xlocale.h>
42 #endif
43 #include <libintl.h>
44 #include <locale.h>
45 #define _(string)       gettext(string)
46 #else
47 #define _(string)       (string)
48 #endif
49
50
51 #define MAXSETUP 11     /* How many setup questions to ask */
52
53 #define UI_TEXT         0       /* Default setup type -- text only */
54 #define UI_DIALOG       2       /* Use the 'dialog' program */
55 #define UI_SILENT       3       /* Silent running, for use in scripts */
56
57 #define SERVICE_NAME    "citadel"
58 #define PROTO_NAME      "tcp"
59 #define NSSCONF         "/etc/nsswitch.conf"
60
61
62 typedef enum _SetupStep {
63         eCitadelHomeDir = 0,
64         eSysAdminName = 1,
65         eSysAdminPW = 2,
66         eUID = 3,
67         eIP_ADDR = 4,
68         eCTDL_Port = 5,
69         eAuthType = 6,
70         eLDAP_Host = 7,
71         eLDAP_Port = 8,
72         eLDAP_Base_DN = 9,
73         eLDAP_Bind_DN = 10,
74         eLDAP_Bind_PW = 11,
75         eMaxQuestions = 12
76 } eSteupStep;
77
78 ///"CREATE_XINETD_ENTRY";
79 const char *EnvNames [eMaxQuestions] = {
80         "SYSADMIN_NAME",
81         "SYSADMIN_PW",
82         "CITADEL_UID",
83         "IP_ADDR",
84         "CITADEL_PORT",
85         "ENABLE_UNIX_AUTH",
86         "LDAP_HOST",
87         "LDAP_PORT",
88         "LDAP_BASE_DN",
89         "LDAP_BIND_DN",
90         "LDAP_BIND_PW"
91 ""
92 };
93
94 int setup_type;
95 int using_web_installer = 0;
96 int enable_home = 1;
97 char admin_pass[SIZ];
98 char admin_cmd[SIZ];
99
100 const char *setup_titles[eMaxQuestions];
101
102 void SetTitles(void)
103 {
104         setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
105         setup_titles[eSysAdminName] = _("Citadel administrator username:");////
106         setup_titles[eSysAdminPW] = _("Administrator password:");//
107         setup_titles[eUID] = _("Citadel User ID:");
108         setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");///
109         setup_titles[eCTDL_Port] = _("Server port number:");
110         setup_titles[eAuthType] = _("Authentication method to use:");////
111         setup_titles[eLDAP_Host] = _("LDAP host:");///
112         setup_titles[eLDAP_Port] = _("LDAP port number:");////
113         setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");///
114         setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");//
115         setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");//
116 }
117
118 /**
119  * \brief print the actual stack frame.
120  */
121 void cit_backtrace(void)
122 {
123 #ifdef HAVE_BACKTRACE
124         void *stack_frames[50];
125         size_t size, i;
126         char **strings;
127
128
129         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
130         strings = backtrace_symbols(stack_frames, size);
131         for (i = 0; i < size; i++) {
132                 if (strings != NULL)
133                         fprintf(stderr, "%s\n", strings[i]);
134                 else
135                         fprintf(stderr, "%p\n", stack_frames[i]);
136         }
137         free(strings);
138 #endif
139 }
140
141 struct config config;
142
143         /* calculate all our path on a central place */
144     /* where to keep our config */
145         
146
147 char *setup_text[] = {
148 #ifndef HAVE_RUN_DIR
149 "Enter the full pathname of the directory in which the Citadel\n"
150 "installation you are creating or updating resides.  If you\n"
151 "specify a directory other than the default, you will need to\n"
152 "specify the -h flag to the server when you start it up.\n",
153 #else
154 "Enter the subdirectory name for an alternate installation of "
155 "Citadel. To do a default installation just leave it blank."
156 "If you specify a directory other than the default, you will need to\n"
157 "specify the -h flag to the server when you start it up.\n"
158 "note that it may not have a leading /",
159 #endif
160
161 "Enter the name of the system administrator (which is probably\n"
162 "you).  When an account is created with this name, it will\n"
163 "automatically be given administrator-level access.\n",
164
165 "Enter a password for the system administrator. When setup\n"
166 "completes it will attempt to create the administrator user\n"
167 "and set the password specified here.\n",
168
169 "Citadel needs to run under its own user ID.  This would\n"
170 "typically be called \"citadel\", but if you are running Citadel\n"
171 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
172 "The server will run under this user ID.  Please specify that\n"
173 "user ID here.  You may specify either a user name or a numeric\n"
174 "UID.\n",
175
176 "Specify the IP address on which your server will run.  If you\n"
177 "leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
178 "on all addresses.  You can usually skip this unless you are\n"
179 "running multiple instances of Citadel on the same computer.\n",
180
181 "Specify the TCP port number on which your server will run.\n"
182 "Normally, this will be port 504, which is the official port\n"
183 "assigned by the IANA for Citadel servers.  You will only need\n"
184 "to specify a different port number if you run multiple instances\n"
185 "of Citadel on the same computer and there is something else\n"
186 "already using port 504.\n",
187
188
189
190 "Specify which authentication mode you wish to use.\n"
191 "\n"
192 " 0. Self contained authentication\n"
193 " 1. Host system integrated authentication\n"
194 " 2. External LDAP - RFC 2307 compliant directory\n"
195 " 3. External LDAP - nonstandard MS Active Directory\n"
196 "\n"
197 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
198 "\n"
199 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n",
200
201 "Please enter the host name or IP address of your LDAP server.\n",
202
203 "Please enter the port number of the LDAP service (usually 389).\n",
204
205 "Please enter the Base DN to search for authentication\n"
206 "(for example: dc=example,dc=com)\n",
207
208 "Please enter the DN of an account to use for binding to the LDAP server\n"
209 "for performing queries.  The account does not require any other\n"
210 "privileges.  If your LDAP server allows anonymous queries, you can.\n"
211 "leave this blank.\n",
212
213 "If you entered a Bind DN in the previous question, you must now enter\n"
214 "the password associated with that account.  Otherwise, you can leave this\n"
215 "blank.\n"
216
217 };
218
219 struct config config;
220 int direction;
221
222
223 void cleanup(int exitcode)
224 {
225 //      printf("Exitcode: %d\n", exitcode);
226 //      cit_backtrace();
227         exit(exitcode);
228 }
229
230
231
232 void title(const char *text)
233 {
234         if (setup_type == UI_TEXT) {
235                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
236         }
237 }
238
239
240
241 int yesno(char *question, int default_value)
242 {
243         int i = 0;
244         int answer = 0;
245         char buf[SIZ];
246
247         switch (setup_type) {
248
249         case UI_TEXT:
250                 do {
251                         printf("%s\nYes/No [%s] --> ",
252                                 question,
253                                 ( default_value ? "Yes" : "No" )
254                         );
255                         if (fgets(buf, sizeof buf, stdin))
256                         {
257                                 answer = tolower(buf[0]);
258                                 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
259                                         answer = default_value;
260                                 else if (answer == 'y')
261                                         answer = 1;
262                                 else if (answer == 'n')
263                                         answer = 0;
264                         }
265                 } while ((answer < 0) || (answer > 1));
266                 break;
267
268         case UI_DIALOG:
269                 sprintf(buf, "exec %s %s --yesno '%s' 15 75",
270                         getenv("CTDL_DIALOG"),
271                         ( default_value ? "" : "--defaultno" ),
272                         question);
273                 i = system(buf);
274                 if (i == 0) {
275                         answer = 1;
276                 }
277                 else {
278                         answer = 0;
279                 }
280                 break;
281         case UI_SILENT:
282                 break;
283
284         }
285         return (answer);
286 }
287
288
289 void important_message(char *title, char *msgtext)
290 {
291         char buf[SIZ];
292         int rv;
293
294         switch (setup_type) {
295
296         case UI_TEXT:
297                 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");
298                 printf("       %s \n\n%s\n\n", title, msgtext);
299                 printf("Press return to continue...");
300                 if (fgets(buf, sizeof buf, stdin));
301                 break;
302
303         case UI_DIALOG:
304                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
305                         getenv("CTDL_DIALOG"),
306                         msgtext);
307                 rv = system(buf);
308                 break;
309         case UI_SILENT:
310                 fprintf(stderr, "%s\n", msgtext);
311                 break;
312         }
313 }
314
315 void important_msgnum(int msgnum)
316 {
317         important_message("Important Message", setup_text[msgnum]);
318 }
319
320 void display_error(char *error_message)
321 {
322         important_message("Error", error_message);
323 }
324
325 void progress(char *text, long int curr, long int cmax)
326 {
327         static long dots_printed = 0L;
328         long a = 0;
329         static FILE *fp = NULL;
330         char buf[SIZ];
331
332         switch (setup_type) {
333
334         case UI_TEXT:
335                 if (curr == 0) {
336                         printf("%s\n", text);
337                         printf("..........................");
338                         printf("..........................");
339                         printf("..........................\r");
340                         fflush(stdout);
341                         dots_printed = 0;
342                 } else if (curr == cmax) {
343                         printf("\r%79s\n", "");
344                 } else {
345                         a = (curr * 100) / cmax;
346                         a = a * 78;
347                         a = a / 100;
348                         while (dots_printed < a) {
349                                 printf("*");
350                                 ++dots_printed;
351                                 fflush(stdout);
352                         }
353                 }
354                 break;
355
356         case UI_DIALOG:
357                 if (curr == 0) {
358                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
359                                 getenv("CTDL_DIALOG"),
360                                 text);
361                         fp = popen(buf, "w");
362                         if (fp != NULL) {
363                                 fprintf(fp, "0\n");
364                                 fflush(fp);
365                         }
366                 } 
367                 else if (curr == cmax) {
368                         if (fp != NULL) {
369                                 fprintf(fp, "100\n");
370                                 pclose(fp);
371                                 fp = NULL;
372                         }
373                 }
374                 else {
375                         a = (curr * 100) / cmax;
376                         if (fp != NULL) {
377                                 fprintf(fp, "%ld\n", a);
378                                 fflush(fp);
379                         }
380                 }
381                 break;
382         case UI_SILENT:
383                 break;
384
385         }
386 }
387
388
389
390 /*
391  * check_services_entry()  -- Make sure "citadel" is in /etc/services
392  *
393  */
394 void check_services_entry(void)
395 {
396         int i;
397         FILE *sfp;
398         char errmsg[256];
399
400         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
401                 for (i=0; i<=2; ++i) {
402                         progress("Adding service entry...", i, 2);
403                         if (i == 0) {
404                                 sfp = fopen("/etc/services", "a");
405                                 if (sfp == NULL) {
406                                         sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
407                                         display_error(errmsg);
408                                 } else {
409                                         fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
410                                         fclose(sfp);
411                                 }
412                         }
413                 }
414         }
415 }
416
417
418
419
420 /*
421  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
422  *
423  */
424 void delete_inittab_entry(void)
425 {
426         FILE *infp;
427         FILE *outfp;
428         char looking_for[256];
429         char buf[1024];
430         char outfilename[32];
431         int changes_made = 0;
432         int rv;
433
434         /* Determine the fully qualified path name of citserver */
435         snprintf(looking_for, 
436                  sizeof looking_for,
437                  "%s/citserver", 
438                  ctdl_sbin_dir
439                 );
440
441         /* Now tweak /etc/inittab */
442         infp = fopen("/etc/inittab", "r");
443         if (infp == NULL) {
444
445                 /* If /etc/inittab does not exist, return quietly.
446                  * Not all host platforms have it.
447                  */
448                 if (errno == ENOENT) {
449                         return;
450                 }
451
452                 /* Other errors might mean something really did go wrong.
453                  */
454                 sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
455                 display_error(buf);
456                 return;
457         }
458
459         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
460         outfp = fdopen(mkstemp(outfilename), "w+");
461         if (outfp == NULL) {
462                 sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
463                 display_error(buf);
464                 fclose(infp);
465                 return;
466         }
467
468         while (fgets(buf, sizeof buf, infp) != NULL) {
469                 if (strstr(buf, looking_for) != NULL) {
470                         rv = fwrite("#", 1, 1, outfp);
471                         ++changes_made;
472                 }
473                 rv = fwrite(buf, strlen(buf), 1, outfp);
474         }
475
476         fclose(infp);
477         fclose(outfp);
478
479         if (changes_made) {
480                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
481                 rv = system(buf);
482                 rv = system("/sbin/init q 2>/dev/null");
483         }
484         else {
485                 unlink(outfilename);
486         }
487 }
488
489
490 /*
491  * install_init_scripts()  -- Try to configure to start Citadel at boot
492  *
493  */
494 void install_init_scripts(void)
495 {
496         struct stat etcinitd;
497         FILE *fp;
498         char *initfile = "/etc/init.d/citadel";
499         char command[SIZ];
500         int rv;
501
502         if ((stat("/etc/init.d/", &etcinitd) == -1) && 
503             (errno == ENOENT))
504         {
505                 if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
506                     (errno == ENOENT))
507                         initfile = CTDLDIR"/citadel.init";
508                 else
509                         initfile = "/etc/rc.d/init.d/citadel";
510         }
511
512         fp = fopen(initfile, "r");
513         if (fp != NULL) {
514                 if (yesno("Citadel already appears to be configured to start at boot.\n"
515                           "Would you like to keep your boot configuration as is?\n", 1) == 1) {
516                         return;
517                 }
518                 fclose(fp);
519                 
520         }
521
522         if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
523                 return;
524         }
525
526         fp = fopen(initfile, "w");
527         if (fp == NULL) {
528                 display_error("Cannot create /etc/init.d/citadel");
529                 return;
530         }
531
532         fprintf(fp,     "#!/bin/sh\n"
533                 "#\n"
534                 "# Init file for Citadel\n"
535                 "#\n"
536                 "# chkconfig: - 79 30\n"
537                 "# description: Citadel service\n"
538                 "# processname: citserver\n"
539                 "# pidfile: %s/citadel.pid\n\n"
540                 "# uncomment this to create coredumps as described in\n"
541                 "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
542                 "# ulimit -c unlimited\n"
543                 "\n"
544                 "CITADEL_DIR=%s\n"
545                 ,
546                 ctdl_run_dir,
547                 ctdl_sbin_dir
548                 );
549         fprintf(fp,     "\n"
550                 "test -d /var/run || exit 0\n"
551                 "\n"
552                 "case \"$1\" in\n"
553                 "\n"
554                 "start)         echo -n \"Starting Citadel... \"\n"
555                 "               if $CITADEL_DIR/citserver -lmail -d\n"
556                 "               then\n"
557                 "                       echo \"ok\"\n"
558                 "               else\n"
559                 "                       echo \"failed\"\n"
560                 "               fi\n");
561         fprintf(fp,     "               ;;\n"
562                 "stop)          echo -n \"Stopping Citadel... \"\n"
563                 "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
564                 "                       echo \"ok\"\n"
565                 "               else\n"
566                 "                       echo \"failed\"\n"
567                 "               fi\n"
568                 "               rm -f %s/citadel.pid 2>/dev/null\n"
569                 ,
570                 ctdl_run_dir
571                 );
572         fprintf(fp,     "               ;;\n"
573                 "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
574                 "                       echo \"ok\"\n"
575                 "               else\n"
576                 "                       echo \"failed\"\n"
577                 "               fi\n"
578                 "               ;;\n"
579                 "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
580                 "               exit 1\n"
581                 "               ;;\n"
582                 "esac\n"
583                 );
584
585         fclose(fp);
586         chmod(initfile, 0755);
587
588         /* Set up the run levels. */
589         rv = system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
590         snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile);
591         rv = system(command);
592         snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile);
593         rv = system(command);
594
595 }
596
597
598
599
600
601
602 /*
603  * On systems which use xinetd, see if we can offer to install Citadel as
604  * the default telnet target.
605  */
606 void check_xinetd_entry(void) {
607         char *filename = "/etc/xinetd.d/telnet";
608         FILE *fp;
609         char buf[SIZ];
610         int already_citadel = 0;
611         int rv;
612
613         fp = fopen(filename, "r+");
614         if (fp == NULL) return;         /* Not there.  Oh well... */
615
616         while (fgets(buf, sizeof buf, fp) != NULL) {
617                 if (strstr(buf, "/citadel") != NULL) already_citadel = 1;
618         }
619         fclose(fp);
620         if (already_citadel) return;    /* Already set up this way. */
621
622         /* Otherwise, prompt the user to create an entry. */
623         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
624                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
625                         return;
626                 }
627         }
628         else {
629                 snprintf(buf, sizeof buf,
630                          "Setup can configure the \"xinetd\" service to automatically\n"
631                          "connect incoming telnet sessions to Citadel, bypassing the\n"
632                          "host system login: prompt.  Would you like to do this?\n"
633                         );
634                 if (yesno(buf, 1) == 0) {
635                         return;
636                 }
637         }
638
639         fp = fopen(filename, "w");
640         fprintf(fp,
641                 "# description: telnet service for Citadel users\n"
642                 "service telnet\n"
643                 "{\n"
644                 "       disable = no\n"
645                 "       flags           = REUSE\n"
646                 "       socket_type     = stream\n"
647                 "       wait            = no\n"
648                 "       user            = root\n"
649                 "       server          = /usr/sbin/in.telnetd\n"
650                 "       server_args     = -h -L %s/citadel\n"
651                 "       log_on_failure  += USERID\n"
652                 "}\n",
653                 ctdl_bin_dir);
654         fclose(fp);
655
656         /* Now try to restart the service */
657         rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
658 }
659
660
661
662 /*
663  * Offer to disable other MTA's
664  */
665 void disable_other_mta(const char *mta) {
666         char buf[SIZ];
667         FILE *fp;
668         int lines = 0;
669         int rv;
670
671         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
672                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
673                 mta, mta);
674         fp = popen(buf, "r");
675         if (fp == NULL) return;
676
677         while (fgets(buf, sizeof buf, fp) != NULL) {
678                 ++lines;
679         }
680         fclose(fp);
681         if (lines == 0) return;         /* Nothing to do. */
682
683
684         /* Offer to replace other MTA with the vastly superior Citadel :)  */
685
686         snprintf(buf, sizeof buf,
687                  "You appear to have the \"%s\" email program\n"
688                  "running on your system.  If you want Citadel mail\n"
689                  "connected with %s, you will have to manually integrate\n"
690                  "them.  It is preferable to disable %s, and use Citadel's\n"
691                  "SMTP, POP3, and IMAP services.\n\n"
692                  "May we disable %s so that Citadel has access to ports\n"
693                  "25, 110, and 143?\n",
694                  mta, mta, mta, mta
695                 );
696         if (yesno(buf, 1) == 0) {
697                 return;
698         }
699         
700
701         sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
702         rv = system(buf);
703         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
704         rv = system(buf);
705 }
706
707 const char *other_mtas[] = {
708         "courier-authdaemon",
709         "courier-imap",
710         "courier-imap-ssl",
711         "courier-pop",
712         "courier-pop3",
713         "courier-pop3d",
714         "cyrmaster",
715         "cyrus",
716         "dovecot",
717         "exim",
718         "exim4",
719         "imapd",
720         "mta",
721         "pop3d",
722         "popd",
723         "postfix",
724         "qmail",
725         "saslauthd",
726         "sendmail",
727         "vmailmgrd",
728         ""
729 };
730
731 void disable_other_mtas(void)
732 {
733         int i = 0;
734         if ((getenv("ACT_AS_MTA") == NULL) || 
735             (getenv("ACT_AS_MTA") &&
736              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
737                 /* Offer to disable other MTA's on the system. */
738                 while (!IsEmptyStr(other_mtas[i]))
739                 {
740                         disable_other_mta(other_mtas[i]);
741                         i++;
742                 }
743         }
744 }
745
746 /* 
747  * Check to see if our server really works.  Returns 0 on success.
748  */
749 int test_server(char *relhomestr, int relhome) {
750         char cmd[256];
751         char cookie[256];
752         FILE *fp;
753         char buf[4096];
754         int found_it = 0;
755
756         /* Generate a silly little cookie.  We're going to write it out
757          * to the server and try to get it back.  The cookie does not
758          * have to be secret ... just unique.
759          */
760         sprintf(cookie, "--test--%d--", getpid());
761
762         if (relhome)
763                 sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
764                         ctdl_sbin_dir,
765                         relhomestr,
766                         cookie);
767         else
768                 sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
769                         ctdl_sbin_dir,
770                         cookie);
771
772         fp = popen(cmd, "r");
773         if (fp == NULL) return(errno);
774
775         while (fgets(buf, sizeof buf, fp) != NULL) {
776                 if ( (buf[0]=='2')
777                      && (strstr(buf, cookie) != NULL) ) {
778                         ++found_it;
779                 }
780         }
781         pclose(fp);
782
783         if (found_it) {
784                 return(0);
785         }
786         return(-1);
787 }
788
789 void strprompt(const char *prompt_title, char *prompt_text, char *Target, char *DefValue)
790 {
791         char buf[SIZ] = "";
792         char setupmsg[SIZ];
793         char dialog_result[PATH_MAX];
794         FILE *fp = NULL;
795         int rv;
796
797         strcpy(setupmsg, "");
798
799         switch (setup_type) {
800         case UI_TEXT:
801                 title(prompt_title);
802                 printf("\n%s\n", prompt_text);
803                 printf("This is currently set to:\n%s\n", Target);
804                 printf("Enter new value or press return to leave unchanged:\n");
805                 if (fgets(buf, sizeof buf, stdin)){
806                         buf[strlen(buf) - 1] = 0;
807                 }
808                 if (!IsEmptyStr(buf))
809                         strcpy(Target, buf);
810                 break;
811
812         case UI_DIALOG:
813                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
814                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
815                         getenv("CTDL_DIALOG"),
816                         prompt_text,
817                         Target,
818                         dialog_result);
819                 rv = system(buf);
820                 fp = fopen(dialog_result, "r");
821                 if (fp != NULL) {
822                         if (fgets(Target, sizeof buf, fp)) {
823                                 if (Target[strlen(Target)-1] == 10) {
824                                         Target[strlen(Target)-1] = 0;
825                                 }
826                         }
827                         fclose(fp);
828                         unlink(dialog_result);
829                 }
830                 break;
831         case UI_SILENT:
832                 strcpy(Target, DefValue);
833                 break;
834         }
835 }
836
837 void set_bool_val(int msgpos, int *ip, char *DefValue) {
838         title(setup_titles[msgpos]);
839         *ip = yesno(setup_text[msgpos], *ip);
840 }
841
842 void set_str_val(int msgpos, char *Target, char *DefValue) {
843         strprompt(setup_titles[msgpos], 
844                   setup_text[msgpos], 
845                   Target, 
846                   DefValue);
847 }
848
849 void set_int_val(int msgpos, int *ip, char *DefValue)
850 {
851         char buf[16];
852         snprintf(buf, sizeof buf, "%d", (int) *ip);
853         set_str_val(msgpos, buf, DefValue);
854         *ip = atoi(buf);
855 }
856
857
858 void set_char_val(int msgpos, char *ip, char *DefValue)
859 {
860         char buf[16];
861         snprintf(buf, sizeof buf, "%d", (int) *ip);
862         set_str_val(msgpos, buf, DefValue);
863         *ip = (char) atoi(buf);
864 }
865
866
867 void set_long_val(int msgpos, long int *ip, char *DefValue)
868 {
869         char buf[16];
870         snprintf(buf, sizeof buf, "%ld", *ip);
871         set_str_val(msgpos, buf, DefValue);
872         *ip = atol(buf);
873 }
874
875
876 void edit_value(int curr)
877 {
878         int i;
879         struct passwd *pw;
880         char ctdluidname[256];
881         char *Value = NULL;
882
883         if (setup_type == UI_SILENT)
884         {
885                 Value = getenv(EnvNames[curr]);
886         }
887
888
889         switch (curr) {
890
891         case eSysAdminName:
892                 set_str_val(curr, config.c_sysadm, Value);
893                 break;
894
895         case eSysAdminPW:
896                 set_str_val(curr, admin_pass, Value);
897                 break;
898         
899         case eUID:
900                 if (setup_type == UI_SILENT)
901                 {               
902                         if (Value) {
903                                 config.c_ctdluid = atoi(Value);
904                         }                                       
905                 }
906                 else
907                 {
908 #ifdef __CYGWIN__
909                         config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
910 #else
911                         i = config.c_ctdluid;
912                         pw = getpwuid(i);
913                         if (pw == NULL) {
914                                 set_int_val(curr, &i, Value);
915                                 config.c_ctdluid = i;
916                         }
917                         else {
918                                 strcpy(ctdluidname, pw->pw_name);
919                                 set_str_val(curr, ctdluidname, Value);
920                                 pw = getpwnam(ctdluidname);
921                                 if (pw != NULL) {
922                                         config.c_ctdluid = pw->pw_uid;
923                                 }
924                                 else if (atoi(ctdluidname) > 0) {
925                                         config.c_ctdluid = atoi(ctdluidname);
926                                 }
927                         }
928 #endif
929                 }
930                 break;
931
932         case eIP_ADDR:
933                 set_str_val(curr, config.c_ip_addr, Value);
934                 break;
935
936         case eCTDL_Port:
937                 set_int_val(curr, &config.c_port_number, Value);
938                 break;
939
940         case eAuthType:
941                 if (setup_type == UI_SILENT)
942                 {
943                         const char *auth;
944                         config.c_auth_mode = AUTHMODE_NATIVE;
945                         auth = Value;
946                         if (auth != NULL)
947                         {
948                                 if ((strcasecmp(auth, "yes") == 0) ||
949                                     (strcasecmp(auth, "host") == 0))
950                                 {
951                                         config.c_auth_mode = AUTHMODE_HOST;
952                                 }
953                                 else if (strcasecmp(auth, "ldap") == 0){
954                                         config.c_auth_mode = AUTHMODE_LDAP;
955                                 }
956                                 else if ((strcasecmp(auth, "ldap_ad") == 0) ||
957                                          (strcasecmp(auth, "active directory") == 0)){
958                                         config.c_auth_mode = AUTHMODE_LDAP_AD;
959                                 }
960                         }
961                 }
962                 else {
963                         set_int_val(curr, &config.c_auth_mode, Value);
964                 }
965                 break;
966
967         case eLDAP_Host:
968                 set_str_val(curr, config.c_ldap_host, Value);
969                 break;
970
971         case eLDAP_Port:
972                 if (config.c_ldap_port == 0) {
973                         config.c_ldap_port = 389;
974                 }
975                 set_int_val(curr, &config.c_ldap_port, Value);
976                 break;
977
978         case eLDAP_Base_DN:
979                 set_str_val(curr, config.c_ldap_base_dn, Value);
980                 break;
981
982         case eLDAP_Bind_DN:
983                 set_str_val(curr, config.c_ldap_bind_dn, Value);
984                 break;
985
986         case eLDAP_Bind_PW:
987                 set_str_val(curr, config.c_ldap_bind_pw, Value);
988                 break;
989
990         }
991 }
992
993 /*
994  * (re-)write the config data to disk
995  */
996 void write_config_to_disk(void)
997 {
998         FILE *fp;
999         int fd;
1000         int rv;
1001
1002         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
1003                 display_error("setup: cannot open citadel.config");
1004                 cleanup(1);
1005         }
1006         fp = fdopen(fd, "wb");
1007         if (fp == NULL) {
1008                 display_error("setup: cannot open citadel.config");
1009                 cleanup(1);
1010         }
1011         rv = fwrite((char *) &config, sizeof(struct config), 1, fp);
1012         fclose(fp);
1013 }
1014
1015
1016
1017
1018 /*
1019  * Figure out what type of user interface we're going to use
1020  */
1021 int discover_ui(void)
1022 {
1023
1024         /* Use "dialog" if we have it */
1025         if (getenv("CTDL_DIALOG") != NULL) {
1026                 return UI_DIALOG;
1027         }
1028                 
1029         return UI_TEXT;
1030 }
1031
1032
1033
1034 void migrate_old_installs(void)
1035 {
1036         int rv;
1037         rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1038         unlink("citadel.log");
1039         unlink("weekly");
1040 }
1041
1042
1043 /*
1044  * Strip "db" entries out of /etc/nsswitch.conf
1045  */
1046 void fixnss(void) {
1047         FILE *fp_read;
1048         int fd_write;
1049         char buf[256];
1050         char buf_nc[256];
1051         char question[512];
1052         int i;
1053         int changed = 0;
1054         int file_changed = 0;
1055         char new_filename[64];
1056         int rv;
1057
1058         fp_read = fopen(NSSCONF, "r");
1059         if (fp_read == NULL) {
1060                 return;
1061         }
1062
1063         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
1064         fd_write = mkstemp(new_filename);
1065         if (fd_write < 0) {
1066                 fclose(fp_read);
1067                 return;
1068         }
1069
1070         while (fgets(buf, sizeof buf, fp_read) != NULL) {
1071                 changed = 0;
1072                 strcpy(buf_nc, buf);
1073                 for (i=0; i<strlen(buf_nc); ++i) {
1074                         if (buf_nc[i] == '#') {
1075                                 buf_nc[i] = 0;
1076                         }
1077                 }
1078                 for (i=0; i<strlen(buf_nc); ++i) {
1079                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
1080                                 if (i > 0) {
1081                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
1082                                                 changed = 1;
1083                                                 file_changed = 1;
1084                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
1085                                                 strcpy(&buf[i], &buf[i+2]);
1086                                                 if (buf[i]==32) {
1087                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
1088                                                         strcpy(&buf[i], &buf[i+1]);
1089                                                 }
1090                                         }
1091                                 }
1092                         }
1093                 }
1094                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
1095                         fclose(fp_read);
1096                         close(fd_write);
1097                         unlink(new_filename);
1098                         return;
1099                 }
1100         }
1101
1102         fclose(fp_read);
1103         
1104         if (!file_changed) {
1105                 unlink(new_filename);
1106                 return;
1107         }
1108
1109         snprintf(question, sizeof question,
1110                 "\n"
1111                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
1112                 "one or more services.  This is not necessary on most systems,\n"
1113                 "and it is known to crash the Citadel server when delivering\n"
1114                 "mail to the Internet.\n"
1115                 "\n"
1116                 "Do you want this module to be automatically disabled?\n"
1117                 "\n"
1118         );
1119
1120         if (yesno(question, 1)) {
1121                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
1122                 rv = system(buf);
1123                 chmod(NSSCONF, 0644);
1124         }
1125         unlink(new_filename);
1126 }
1127
1128 void check_init_script (char *relhome)
1129 {
1130         int rv;
1131         FILE *fp;
1132
1133         /* 
1134          * If we're running on SysV, install init scripts.
1135          */
1136         if (!access("/var/run", W_OK)) {
1137
1138                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1139                         install_init_scripts();
1140                 }
1141
1142                 if (!access("/etc/init.d/citadel", X_OK)) {
1143                         rv = system("/etc/init.d/citadel start");
1144                         sleep(3);
1145                 }
1146
1147                 if (test_server(relhome, enable_home) == 0) {
1148                         char buf[SIZ];
1149                         int found_it = 0;
1150
1151                         if (config.c_auth_mode == AUTHMODE_NATIVE) {
1152                                 snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
1153                                         ctdl_sbin_dir, config.c_sysadm, admin_pass);
1154                                 fp = popen(admin_cmd, "r");
1155                                 if (fp != NULL) {
1156                                         while (fgets(buf, sizeof buf, fp) != NULL) 
1157                                         {
1158                                                 if ((atol(buf) == 574) || (atol(buf) == 200))
1159                                                         ++found_it;
1160                                         }
1161                                         pclose(fp);
1162                                 }
1163                         
1164                                 if (found_it == 0) {
1165                                         important_message("Error","Setup failed to create your admin user");
1166                                 }
1167                         }
1168
1169                         if (setup_type != UI_SILENT)
1170                                 important_message("Setup finished",
1171                                                   "Setup of the Citadel server is complete.\n"
1172                                                   "If you will be using WebCit, please run its\n"
1173                                                   "setup program now; otherwise, run './citadel'\n"
1174                                                   "to log in.\n");
1175                 }
1176                 else {
1177                         important_message("Setup failed",
1178                                 "Setup is finished, but the Citadel server failed to start.\n"
1179                                 "Go back and check your configuration.\n"
1180                         );
1181                 }
1182
1183         }
1184
1185         else {
1186                 important_message("Setup finished",
1187                         "Setup is finished.  You may now start the server.");
1188         }
1189 }
1190
1191 void set_default_values(void)
1192 {
1193         struct passwd *pw;
1194         struct utsname my_utsname;
1195         struct hostent *he;
1196
1197         /* Determine our host name, in case we need to use it as a default */
1198         uname(&my_utsname);
1199
1200         /* set some sample/default values in place of blanks... */
1201         if (IsEmptyStr(config.c_nodename))
1202                 safestrncpy(config.c_nodename, my_utsname.nodename,
1203                             sizeof config.c_nodename);
1204         strtok(config.c_nodename, ".");
1205         if (IsEmptyStr(config.c_fqdn) ) {
1206                 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
1207                         safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
1208                 } else {
1209                         safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
1210                 }
1211         }
1212         if (IsEmptyStr(config.c_humannode)) {
1213                 strcpy(config.c_humannode, "My System");
1214         }
1215         if (IsEmptyStr(config.c_phonenum)) {
1216                 strcpy(config.c_phonenum, "US 800 555 1212");
1217         }
1218         if (config.c_initax == 0) {
1219                 config.c_initax = 4;
1220         }
1221         if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
1222         if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
1223         if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
1224         if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
1225         if (config.c_port_number == 0) {
1226                 config.c_port_number = 504;
1227         }
1228         if (config.c_sleeping == 0) {
1229                 config.c_sleeping = 900;
1230         }
1231         if (config.c_ctdluid == 0) {
1232                 pw = getpwnam("citadel");
1233                 if (pw != NULL) {
1234                         config.c_ctdluid = pw->pw_uid;
1235                 }
1236         }
1237         if (config.c_ctdluid == 0) {
1238                 pw = getpwnam("bbs");
1239                 if (pw != NULL) {
1240                         config.c_ctdluid = pw->pw_uid;
1241                 }
1242         }
1243         if (config.c_ctdluid == 0) {
1244                 pw = getpwnam("guest");
1245                 if (pw != NULL) {
1246                         config.c_ctdluid = pw->pw_uid;
1247                 }
1248         }
1249         if (config.c_createax == 0) {
1250                 config.c_createax = 3;
1251         }
1252         /*
1253          * Negative values for maxsessions are not allowed.
1254          */
1255         if (config.c_maxsessions < 0) {
1256                 config.c_maxsessions = 0;
1257         }
1258         /* We need a system default message expiry policy, because this is
1259          * the top level and there's no 'higher' policy to fall back on.
1260          * By default, do not expire messages at all.
1261          */
1262         if (config.c_ep.expire_mode == 0) {
1263                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1264                 config.c_ep.expire_value = 0;
1265         }
1266
1267         /*
1268          * Default port numbers for various services
1269          */
1270         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1271         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1272         if (config.c_imap_port == 0) config.c_imap_port = 143;
1273         if (config.c_msa_port == 0) config.c_msa_port = 587;
1274         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1275         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1276         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1277         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1278         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1279         if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
1280         if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
1281 }
1282
1283
1284 void get_config (void)
1285 {
1286         int a;
1287         int rv;
1288         FILE *fp;
1289
1290         /*
1291          * What we're going to try to do here is append a whole bunch of
1292          * nulls to the citadel.config file, so we can keep the old config
1293          * values if they exist, but if the file is missing or from an
1294          * earlier version with a shorter config structure, when setup tries
1295          * to read the old config parameters, they'll all come up zero.
1296          * The length of the config file will be set to what it's supposed
1297          * to be when we rewrite it, because we replace the old file with a
1298          * completely new copy.
1299          */
1300         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1301                       S_IRUSR | S_IWUSR)) == -1) {
1302                 display_error("setup: cannot append citadel.config");
1303                 cleanup(errno);
1304         }
1305         fp = fdopen(a, "ab");
1306         if (fp == NULL) {
1307                 display_error("setup: cannot append citadel.config");
1308                 cleanup(errno);
1309         }
1310         for (a = 0; a < sizeof(struct config); ++a) {
1311                 putc(0, fp);
1312         }
1313         fclose(fp);
1314
1315         /* now we re-open it, and read the old or blank configuration */
1316         fp = fopen(file_citadel_config, "rb");
1317         if (fp == NULL) {
1318                 display_error("setup: cannot open citadel.config");
1319                 cleanup(errno);
1320         }
1321         rv = fread((char *) &config, sizeof(struct config), 1, fp);
1322         fclose(fp);
1323
1324 }
1325
1326 int main(int argc, char *argv[])
1327 {
1328         int a;
1329         int curr; 
1330         char aaa[128];
1331         int old_setup_level = 0;
1332         int info_only = 0;
1333         int relh=0;
1334         int home=0;
1335         char relhome[PATH_MAX]="";
1336         char ctdldir[PATH_MAX]=CTDLDIR;
1337         int rv;
1338         struct passwd *pw;
1339         gid_t gid;
1340         
1341         /* set an invalid setup type */
1342         setup_type = (-1);
1343
1344         /* Check to see if we're running the web installer */
1345         if (getenv("CITADEL_INSTALLER") != NULL) {
1346                 using_web_installer = 1;
1347         }
1348
1349         /* parse command line args */
1350         for (a = 0; a < argc; ++a) {
1351                 if (!strncmp(argv[a], "-u", 2)) {
1352                         strcpy(aaa, argv[a]);
1353                         strcpy(aaa, &aaa[2]);
1354                         setup_type = atoi(aaa);
1355                 }
1356                 else if (!strcmp(argv[a], "-i")) {
1357                         info_only = 1;
1358                 }
1359                 else if (!strcmp(argv[a], "-q")) {
1360                         setup_type = UI_SILENT;
1361                 }
1362                 else if (!strncmp(argv[a], "-h", 2)) {
1363                         relh=argv[a][2]!='/';
1364                         if (!relh) {
1365                                 safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
1366                         } else {
1367                                 safestrncpy(relhome, &argv[a][2], sizeof relhome);
1368                         }
1369                         home = 1;
1370                 }
1371
1372         }
1373
1374         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
1375         SetTitles();
1376
1377         /* If a setup type was not specified, try to determine automatically
1378          * the best one to use out of all available types.
1379          */
1380         if (setup_type < 0) {
1381                 setup_type = discover_ui();
1382         }
1383         if (info_only == 1) {
1384                 important_message("Citadel Setup", CITADEL);
1385                 cleanup(0);
1386         }
1387
1388         enable_home = ( relh | home );
1389
1390         if (chdir(ctdl_run_dir) != 0) {
1391                 char errmsg[SIZ];
1392                 sprintf(errmsg, "The directory you specified does not exist: [%s]\n", ctdl_run_dir);
1393                 
1394                 important_message("Citadel Setup", errmsg);
1395                 cleanup(errno);
1396         }
1397
1398
1399         /* Try to stop Citadel if we can */
1400         if (!access("/etc/init.d/citadel", X_OK)) {
1401                 rv = system("/etc/init.d/citadel stop");
1402         }
1403
1404         /* Make sure Citadel is not running. */
1405         if (test_server(relhome, enable_home) == 0) {
1406                 important_message("Citadel Setup",
1407                         "The Citadel service is still running.\n"
1408                         "Please stop the service manually and run "
1409                         "setup again.");
1410                 cleanup(1);
1411         }
1412
1413         /* Now begin. */
1414         switch (setup_type) {
1415
1416         case UI_TEXT:
1417                 printf("\n\n\n"
1418                         "              *** Citadel setup program ***\n\n");
1419                 break;
1420
1421         }
1422
1423         get_config ();
1424
1425         set_default_values();
1426
1427         /* Go through a series of dialogs prompting for config info */
1428         for (curr = 1; curr <= MAXSETUP; ++curr) {
1429                 edit_value(curr);
1430                 if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
1431                         curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
1432                 }
1433         }
1434
1435 /***** begin version update section ***** */
1436         /* take care of any updating that is necessary */
1437
1438         old_setup_level = config.c_setup_level;
1439
1440         if (old_setup_level == 0) {
1441                 goto NEW_INST;
1442         }
1443
1444         if (old_setup_level < 555) {
1445                 important_message("Citadel Setup",
1446                                   "This Citadel installation is too old "
1447                                   "to be upgraded.");
1448                 cleanup(1);
1449         }
1450         write_config_to_disk();
1451
1452         old_setup_level = config.c_setup_level;
1453
1454         /* end of version update section */
1455
1456 NEW_INST:
1457         config.c_setup_level = REV_LEVEL;
1458
1459 /******************************************/
1460         if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
1461                 gid = getgid();
1462         } else {
1463                 gid = pw->pw_gid;
1464         }
1465
1466         create_run_directories(config.c_ctdluid, gid);
1467
1468         write_config_to_disk();
1469
1470         migrate_old_installs(); /* Delete files and directories used by older Citadel versions */
1471
1472         if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
1473             (setup_type != UI_SILENT))
1474                 check_services_entry(); /* Check /etc/services */
1475 #ifndef __CYGWIN__
1476         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1477         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1478         disable_other_mtas();   /* Offer to disable other MTAs */
1479
1480 #endif
1481         fixnss();       /* Check for the 'db' nss and offer to disable it */
1482
1483         progress("Setting file permissions", 1, 3);
1484         rv = chown(file_citadel_config, config.c_ctdluid, gid);
1485         progress("Setting file permissions", 2, 3);
1486         rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1487         progress("Setting file permissions", 3, 3);
1488
1489         check_init_script (relhome);
1490         cleanup(0);
1491         return 0;
1492 }
1493
1494