1 // Citadel setup utility
3 // Copyright (c) 1987-2022 by the citadel.org team
5 // This program is open source software. Use, duplication, or disclosure
6 // is subject to the terms of the GNU General Public License, version 3.
7 // The program is distributed without any warranty, expressed or implied.
9 #define SHOW_ME_VAPPEND_PRINTF
16 #include <sys/types.h>
25 #include <sys/socket.h>
28 #include <libcitadel.h>
29 #include "../server/citadel.h"
31 #include "../server/sysdep.h"
32 #include "../server/citadel_dirs.h"
40 #define _(string) gettext(string)
42 #define _(string) (string)
45 #define SERVICE_NAME "citadel"
46 #define PROTO_NAME "tcp"
47 #define NSSCONF "/etc/nsswitch.conf"
49 typedef enum _SetupStep {
65 ///"CREATE_XINETD_ENTRY";
66 /* Environment variables, don't translate! */
67 const char *EnvNames [eMaxQuestions] = {
82 int setup_type = (-1);
87 int serv_sock = (-1) ;
89 const char *setup_titles[eMaxQuestions];
90 const char *setup_text[eMaxQuestions];
94 void SetTitles(void) {
103 setlocale(LC_MESSAGES, getenv("LANG"));
104 bindtextdomain("citadel-setup", LOCALEDIR"/locale");
105 textdomain("citadel-setup");
106 bind_textdomain_codeset("citadel-setup","UTF8");
109 setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
111 setup_text[eCitadelHomeDir] = _(
112 "Enter the full pathname of the directory in which the Citadel\n"
113 "installation you are creating or updating resides. If you\n"
114 "specify a directory other than the default, you will need to\n"
115 "specify the -h flag to the server when you start it up.\n");
117 setup_text[eCitadelHomeDir] = _(
118 "Enter the subdirectory name for an alternate installation of "
119 "Citadel. To do a default installation just leave it blank."
120 "If you specify a directory other than the default, you will need to\n"
121 "specify the -h flag to the server when you start it up.\n"
122 "note that it may not have a leading /");
124 setup_titles[eSysAdminName] = _("Citadel administrator username:");
125 setup_text[eSysAdminName] = _(
126 "Please enter the name of the Citadel user account that should be granted "
127 "administrative privileges once created. If using internal authentication "
128 "this user account will be created if it does not exist. For external "
129 "authentication this user account has to exist.");
131 setup_titles[eSysAdminPW] = _("Administrator password:");
132 setup_text[eSysAdminPW] = _(
133 "Enter a password for the system administrator. When setup\n"
134 "completes it will attempt to create the administrator user\n"
135 "and set the password specified here.\n");
137 setup_titles[eUID] = _("Citadel User ID:");
138 setup_text[eUID] = _(
139 "Citadel needs to run under its own user ID. This would\n"
140 "typically be called \"citadel\", but if you are running Citadel\n"
141 "as a public site, you might also call it \"bbs\" or \"guest\".\n"
142 "The server will run under this user ID. Please specify that\n"
143 "user ID here. You may specify either a user name or a numeric\n"
146 setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
147 setup_text[eIP_ADDR] = _(
148 "Please specify the IP address which the server should be listening to. "
149 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
150 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
151 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
152 "listen on all addresses. "
153 "This can usually be left to the default unless multiple instances of Citadel "
154 "are running on the same computer.");
156 setup_titles[eCTDL_Port] = _("Server port number:");
157 setup_text[eCTDL_Port] = _(
158 "Specify the TCP port number on which your server will run.\n"
159 "Normally, this will be port 504, which is the official port\n"
160 "assigned by the IANA for Citadel servers. You will only need\n"
161 "to specify a different port number if you run multiple instances\n"
162 "of Citadel on the same computer and there is something else\n"
163 "already using port 504.\n");
165 setup_titles[eAuthType] = _("Authentication method to use:");
166 setup_text[eAuthType] = _(
167 "Please choose the user authentication mode. By default Citadel will use its "
168 "own internal user accounts database. If you choose Host, Citadel users will "
169 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
170 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
171 "chooses the nonstandard MS Active Directory LDAP scheme."
173 "Do not change this option unless you are sure it is required, since changing "
174 "back requires a full reinstall of Citadel."
176 " 0. Self contained authentication\n"
177 " 1. Host system integrated authentication\n"
178 " 2. External LDAP - RFC 2307 POSIX schema\n"
179 " 3. External LDAP - MS Active Directory schema\n"
181 "For help: http://www.citadel.org/authmodes.html\n"
183 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
185 setup_titles[eLDAP_Host] = _("LDAP host:");
186 setup_text[eLDAP_Host] = _(
187 "Please enter the host name or IP address of your LDAP server.\n");
189 setup_titles[eLDAP_Port] = _("LDAP port number:");
190 setup_text[eLDAP_Port] = _(
191 "Please enter the port number of the LDAP service (usually 389).\n");
193 setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
194 setup_text[eLDAP_Base_DN] = _(
195 "Please enter the Base DN to search for authentication\n"
196 "(for example: dc=example,dc=com)\n");
198 setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
199 setup_text[eLDAP_Bind_DN] = _(
200 "Please enter the DN of an account to use for binding to the LDAP server for "
201 "performing queries. The account does not require any other privileges. If "
202 "your LDAP server allows anonymous queries, you can leave this blank.\n");
204 setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
205 setup_text[eLDAP_Bind_PW] = _(
206 "If you entered a Bind DN in the previous question, you must now enter\n"
207 "the password associated with that account. Otherwise, you can leave this\n"
213 printf("\033[2J\033[H\033[44m\033[1m\033[K\n");
214 printf(" %s \033[K\n", program_title);
220 void title(const char *text) {
222 printf("\033[1m\033[32m<\033[33m%s\033[32m>\033[0m\n", text);
226 int yesno(const char *question, int default_value) {
231 printf("\033[31m\033[32m%s\n%s [\033[33m%s\033[32m]\033[0m --> ", question, _("Yes/No"), ( default_value ? _("Yes") : _("No") ));
232 if (fgets(buf, sizeof buf, stdin)) {
233 answer = tolower(buf[0]);
234 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
235 answer = default_value;
237 else if (answer == 'y') {
240 else if (answer == 'n') {
244 } while ((answer < 0) || (answer > 1));
249 void important_message(const char *title, const char *msgtext) {
253 printf("%s\n%s\n\n", title, msgtext);
254 printf("%s", _("Press return to continue..."));
255 if (fgets(buf, sizeof buf, stdin)) {
261 void important_msgnum(int msgnum) {
262 important_message(_("Important Message"), setup_text[msgnum]);
266 void display_error(char *error_message_format, ...) {
271 va_start(arg_ptr, error_message_format);
272 StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
275 important_message(_("Error"), ChrPtr(Msg));
280 void progress(char *text, long int curr, long int cmax) {
286 printf("%s\n", text);
287 printf("\033[1m\033[33m[\033[32m............................................................................\033[33m]\033[0m\r");
289 else if (curr == cmax) {
290 printf("\r%79s\n", "");
293 printf("\033[1m\033[33m[\033[32m");
294 a = (curr * 100) / cmax;
297 for (i=0; i<a; ++i) {
306 int uds_connectsock(char *sockpath) {
308 struct sockaddr_un addr;
310 memset(&addr, 0, sizeof(addr));
311 addr.sun_family = AF_UNIX;
312 strcpy(addr.sun_path, sockpath);
314 s = socket(AF_UNIX, SOCK_STREAM, 0);
319 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
328 // input binary data from socket
329 void serv_read(char *buf, int bytes) {
333 while (len < bytes) {
334 rlen = read(serv_sock, &buf[len], bytes - len);
343 // send binary to server
344 void serv_write(char *buf, int nbytes) {
345 int bytes_written = 0;
347 while (bytes_written < nbytes) {
348 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
352 bytes_written = bytes_written + retval;
357 // input string from socket - implemented in terms of serv_read()
358 void serv_gets(char *buf) {
361 // Read one character at a time.
363 serv_read(&buf[i], 1);
364 if (buf[i] == '\n' || i == (SIZ-1))
368 // If we got a long line, discard characters until the newline.
370 while (buf[i] != '\n') {
371 serv_read(&buf[i], 1);
375 // Strip all trailing nonprintables (crlf)
380 // send line to server - implemented in terms of serv_write()
381 void serv_puts(char *buf) {
382 serv_write(buf, strlen(buf));
387 // Convenience functions to get/set system configuration entries
388 void getconf_str(char *buf, char *key) {
392 sprintf(cmd, "CONF GETVAL|%s", key);
396 extract_token(buf, &ret[4], 0, '|', SIZ);
404 int getconf_int(char *key) {
406 getconf_str(buf, key);
411 void setconf_str(char *key, char *val) {
414 sprintf(buf, "CONF PUTVAL|%s|%s", key, val);
420 void setconf_int(char *key, int val) {
423 sprintf(buf, "CONF PUTVAL|%s|%d", key, val);
429 // On systems which use xinetd, see if we can offer to install Citadel as
430 // the default telnet target.
431 void check_xinetd_entry(void) {
432 char *filename = "/etc/xinetd.d/telnet";
435 int already_citadel = 0;
438 fp = fopen(filename, "r+");
439 if (fp == NULL) return; // Not there. Oh well...
441 while (fgets(buf, sizeof buf, fp) != NULL) {
442 if (strstr(buf, "/citadel") != NULL) {
447 if (already_citadel) return; // Already set up this way.
449 // Otherwise, prompt the user to create an entry.
450 if (getenv("CREATE_XINETD_ENTRY") != NULL) {
451 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
456 snprintf(buf, sizeof buf,
457 _("Setup can configure the \"xinetd\" service to automatically\n"
458 "connect incoming telnet sessions to Citadel, bypassing the\n"
459 "host system login: prompt. Would you like to do this?\n"
462 if (yesno(buf, 1) == 0) {
467 fp = fopen(filename, "w");
469 "# description: telnet service for Citadel users\n"
474 " socket_type = stream\n"
477 " server = /usr/sbin/in.telnetd\n"
478 " server_args = -h -L %s/citadel\n"
479 " log_on_failure += USERID\n"
485 // Now try to restart the service. (This only works on systemd; others will need to restart it manually.)
486 rv = system("systemctl restart xinetd >/dev/null 2>&1");
488 rv = system("service xinetd restart >/dev/null 2>&1");
491 display_error(_("failed to restart xinetd.\n"));
496 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue) {
500 strcpy(setupmsg, "");
503 printf("\n%s\n", prompt_text);
504 printf("%s\n%s\n", _("This is currently set to:"), Target);
505 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
506 if (fgets(buf, sizeof buf, stdin)) {
507 buf[strlen(buf) - 1] = 0;
509 if (!IsEmptyStr(buf)) {
515 void set_bool_val(int msgpos, int *ip, char *DefValue) {
516 title(setup_titles[msgpos]);
517 *ip = yesno(setup_text[msgpos], *ip);
521 void set_str_val(int msgpos, char *Target, char *DefValue) {
522 strprompt(setup_titles[msgpos],
530 // like set_str_val() but for numeric values
531 void set_int_val(int msgpos, int *target, char *default_value) {
533 sprintf(buf, "%d", *target);
535 set_str_val(msgpos, buf, default_value);
536 } while ( (strcmp(buf, "0")) && (atoi(buf) == 0) );
541 void edit_value(int curr) {
542 struct passwd *pw = NULL;
543 char ctdluidname[256];
545 char *default_value = NULL;
551 if (default_value == NULL) {
558 getconf_str(admin_name, "c_sysadm");
559 set_str_val(curr, admin_name, default_value);
560 setconf_str("c_sysadm", admin_name);
564 set_str_val(curr, admin_pass, default_value);
568 ctdluid = getconf_int("c_ctdluid");
569 pw = getpwuid(ctdluid);
571 set_int_val(curr, &ctdluid, default_value);
574 strcpy(ctdluidname, pw->pw_name);
575 set_str_val(curr, ctdluidname, default_value);
576 pw = getpwnam(ctdluidname);
578 ctdluid = pw->pw_uid;
580 else if (atoi(ctdluidname) > 0) {
581 ctdluid = atoi(ctdluidname);
584 setconf_int("c_ctdluid", ctdluid);
588 getconf_str(buf, "c_ip_addr");
589 set_str_val(curr, buf, default_value);
590 setconf_str("c_ip_addr", buf);
594 portnum = getconf_int("c_port_number");
595 set_int_val(curr, &portnum, default_value);
596 setconf_int("c_port_number", portnum);
600 auth = getconf_int("c_auth_mode");
601 set_int_val(curr, &auth, default_value);
602 setconf_int("c_auth_mode", auth);
606 getconf_str(buf, "c_ldap_host");
607 if (IsEmptyStr(buf)) {
608 strcpy(buf, "localhost");
610 set_str_val(curr, buf, default_value);
611 setconf_str("c_ldap_host", buf);
615 lportnum = getconf_int("c_ldap_port");
619 set_int_val(curr, &lportnum, default_value);
620 setconf_int("c_ldap_port", lportnum);
624 getconf_str(buf, "c_ldap_base_dn");
625 set_str_val(curr, buf, default_value);
626 setconf_str("c_ldap_base_dn", buf);
630 getconf_str(buf, "c_ldap_bind_dn");
631 set_str_val(curr, buf, default_value);
632 setconf_str("c_ldap_bind_dn", buf);
636 getconf_str(buf, "c_ldap_bind_pw");
637 set_str_val(curr, buf, default_value);
638 setconf_str("c_ldap_bind_pw", buf);
644 // Messages that are no longer in use.
645 // We keep them here so we don't lose the translations if we need them later.
647 important_message(_("Setup finished"),
648 _("Setup of the Citadel server is complete.\n"
649 "If you will be using WebCit, please run its\n"
650 "setup program now; otherwise, run './citadel'\n"
652 important_message(_("Setup failed"),
653 _("Setup is finished, but the Citadel server failed to start.\n"
654 "Go back and check your configuration.\n");
655 important_message(_("Setup finished"),
656 _("Setup is finished. You may now start the server."));
660 int main(int argc, char *argv[]) {
665 char ctdldir[PATH_MAX]=CTDLDIR;
668 char *activity = NULL;
670 // Keep a mild groove on
671 program_title = _("Citadel Server setup");
673 // set an invalid setup type
676 // parse command line args
677 for (a = 0; a < argc; ++a) {
678 if (!strncmp(argv[a], "-u", 2)) {
679 strcpy(aaa, argv[a]);
680 strcpy(aaa, &aaa[2]);
681 setup_type = atoi(aaa);
683 else if (!strncmp(argv[a], "-h", 2)) {
684 safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
688 if (chdir(ctdldir) != 0) {
689 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
695 // Connect to the running Citadel server.
696 char *connectingmsg = _("Connecting to Citadel server");
697 for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) { // wait for server to start up
698 progress(connectingmsg, i, 30);
699 serv_sock = uds_connectsock(file_citadel_admin_socket);
702 progress(connectingmsg, 30, 30);
707 _("Setup could not connect to a running Citadel server."),
708 strerror(errno), file_citadel_admin_socket
713 // read the server greeting
716 display_error("%s\n", buf);
720 // Are we connected to the correct Citadel server?
724 display_error("%s\n", buf);
728 while (serv_gets(buf), strcmp(buf, "000")) {
730 if (atoi(buf) != REV_LEVEL) {
731 display_error("%s\n", _("Your setup program and Citadel server are from different versions."));
738 printf("\n\n\n *** %s ***\n\n", program_title);
740 // Go through a series of dialogs prompting for config info
741 for (curr = 1; curr < eMaxQuestions; ++curr) {
744 if ( (curr == eAuthType)
745 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
746 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
748 curr += 5; // skip LDAP questions if we're not authenticating against LDAP
751 if (curr == eSysAdminName) {
752 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) { // for native auth mode, fetch the admin's existing pw
753 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
757 extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
761 ++curr; // skip the password question for non-native auth modes
766 if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
773 // setup now must be run after Citadel Server is already running, so we don't need this anymore.
774 //if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
775 //display_error("%s\n", _("failed to create directories"));
778 activity = _("Reconfiguring Citadel server");
779 progress(activity, 0, 5);
780 sleep(1); // Let the message appear briefly
782 // Create the administrator account. It's ok if the command fails if this user already exists.
783 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
784 progress(activity, 1, 5);
785 snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
787 progress(activity, 2, 5);
790 progress(activity, 3, 5);
792 // Assign the desired password and access level to the administrator account.
793 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
794 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
796 progress(activity, 4, 5);
799 int admin_flags = extract_int(&buf[4], 2);
800 int admin_times_called = extract_int(&buf[4], 3);
801 int admin_msgs_posted = extract_int(&buf[4], 4);
802 snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
803 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
809 progress(activity, 5, 5);
811 check_xinetd_entry(); // Check /etc/xinetd.d/telnet
814 activity = _("Restarting Citadel server to apply changes");
815 progress(activity, 0, 51);
819 long original_start_time = extract_long(&buf[4], 3);
821 progress(activity, 1, 51);
823 progress(activity, 2, 51);
826 display_error("%s\n", buf);
833 for (i=3; i<=6; ++i) { // wait for server to shut down
834 progress(activity, i, 51);
838 for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) { // wait for server to start up
839 progress(activity, i, 51);
840 serv_sock = uds_connectsock(file_citadel_admin_socket);
844 progress(activity, 49, 51);
847 progress(activity, 50, 51);
850 long new_start_time = extract_long(&buf[4], 3);
853 progress(activity, 51, 51);
855 if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
856 display_error("%s\n", _("Setup failed to restart Citadel server. Please restart it manually."));