1 // Citadel setup utility
3 // Copyright (c) 1987-2024 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.
8 #define SHOW_ME_VAPPEND_PRINTF
15 #include <sys/types.h>
24 #include <sys/socket.h>
27 #include <libcitadel.h>
28 #include "../server/citadel_defs.h"
29 #include "../server/server.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.
646 void unused_messages(void) {
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"
653 important_message(_("Setup failed"),
654 _("Setup is finished, but the Citadel server failed to start.\n"
655 "Go back and check your configuration.\n")
657 important_message(_("Setup finished"),
658 _("Setup is finished. You may now start the server.")
663 int main(int argc, char *argv[]) {
668 char ctdldir[PATH_MAX]=CTDLDIR;
671 char *activity = NULL;
673 // Keep a mild groove on
674 program_title = _("Citadel Server setup");
676 // set an invalid setup type
679 // parse command line args
680 for (a = 0; a < argc; ++a) {
681 if (!strncmp(argv[a], "-u", 2)) {
682 strcpy(aaa, argv[a]);
683 strcpy(aaa, &aaa[2]);
684 setup_type = atoi(aaa);
686 else if (!strncmp(argv[a], "-h", 2)) {
687 safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
691 if (chdir(ctdldir) != 0) {
692 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
698 // Connect to the running Citadel server.
699 char *connectingmsg = _("Connecting to Citadel server");
700 for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) { // wait for server to start up
701 progress(connectingmsg, i, 30);
702 serv_sock = uds_connectsock(file_citadel_admin_socket);
705 progress(connectingmsg, 30, 30);
710 _("Setup could not connect to a running Citadel server."),
711 strerror(errno), file_citadel_admin_socket
716 // read the server greeting
719 display_error("%s\n", buf);
723 // Are we connected to the correct Citadel server?
727 display_error("%s\n", buf);
731 while (serv_gets(buf), strcmp(buf, "000")) {
733 if (atoi(buf) != REV_LEVEL) {
734 display_error("%s\n", _("Your setup program and Citadel server are from different versions."));
741 printf("\n\n\n *** %s ***\n\n", program_title);
743 // Go through a series of dialogs prompting for config info
744 for (curr = 1; curr < eMaxQuestions; ++curr) {
747 if ( (curr == eAuthType)
748 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
749 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
751 curr += 5; // skip LDAP questions if we're not authenticating against LDAP
754 if (curr == eSysAdminName) {
755 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) { // for native auth mode, fetch the admin's existing pw
756 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
760 extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
764 ++curr; // skip the password question for non-native auth modes
769 if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
776 // setup now must be run after Citadel Server is already running, so we don't need this anymore.
777 //if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
778 //display_error("%s\n", _("failed to create directories"));
781 activity = _("Reconfiguring Citadel server");
782 progress(activity, 0, 5);
783 sleep(1); // Let the message appear briefly
785 // Create the administrator account. It's ok if the command fails if this user already exists.
786 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
787 progress(activity, 1, 5);
788 snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
790 progress(activity, 2, 5);
793 progress(activity, 3, 5);
795 // Assign the desired password and access level to the administrator account.
796 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
797 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
799 progress(activity, 4, 5);
802 int admin_flags = extract_int(&buf[4], 2);
803 int admin_times_called = extract_int(&buf[4], 3);
804 int admin_msgs_posted = extract_int(&buf[4], 4);
805 snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
806 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
812 progress(activity, 5, 5);
814 check_xinetd_entry(); // Check /etc/xinetd.d/telnet
817 activity = _("Restarting Citadel server to apply changes");
818 progress(activity, 0, 51);
822 long original_start_time = extract_long(&buf[4], 3);
824 progress(activity, 1, 51);
826 progress(activity, 2, 51);
829 display_error("%s\n", buf);
836 for (i=3; i<=6; ++i) { // wait for server to shut down
837 progress(activity, i, 51);
841 for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) { // wait for server to start up
842 progress(activity, i, 51);
843 serv_sock = uds_connectsock(file_citadel_admin_socket);
847 progress(activity, 49, 51);
850 progress(activity, 50, 51);
853 long new_start_time = extract_long(&buf[4], 3);
856 progress(activity, 51, 51);
858 if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
859 display_error("%s\n", _("Setup failed to restart Citadel server. Please restart it manually."));