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.
8 #define SHOW_ME_VAPPEND_PRINTF
15 #include <sys/types.h>
24 #include <sys/socket.h>
27 #include <libcitadel.h>
28 #include "../server/citadel.h"
30 #include "../server/sysdep.h"
31 #include "../server/citadel_dirs.h"
39 #define _(string) gettext(string)
41 #define _(string) (string)
44 #define SERVICE_NAME "citadel"
45 #define PROTO_NAME "tcp"
46 #define NSSCONF "/etc/nsswitch.conf"
48 typedef enum _SetupStep {
64 ///"CREATE_XINETD_ENTRY";
65 /* Environment variables, don't translate! */
66 const char *EnvNames [eMaxQuestions] = {
81 int setup_type = (-1);
86 int serv_sock = (-1) ;
88 const char *setup_titles[eMaxQuestions];
89 const char *setup_text[eMaxQuestions];
93 void SetTitles(void) {
102 setlocale(LC_MESSAGES, getenv("LANG"));
103 bindtextdomain("citadel-setup", LOCALEDIR"/locale");
104 textdomain("citadel-setup");
105 bind_textdomain_codeset("citadel-setup","UTF8");
108 setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
110 setup_text[eCitadelHomeDir] = _(
111 "Enter the full pathname of the directory in which the Citadel\n"
112 "installation you are creating or updating resides. If you\n"
113 "specify a directory other than the default, you will need to\n"
114 "specify the -h flag to the server when you start it up.\n");
116 setup_text[eCitadelHomeDir] = _(
117 "Enter the subdirectory name for an alternate installation of "
118 "Citadel. To do a default installation just leave it blank."
119 "If you specify a directory other than the default, you will need to\n"
120 "specify the -h flag to the server when you start it up.\n"
121 "note that it may not have a leading /");
123 setup_titles[eSysAdminName] = _("Citadel administrator username:");
124 setup_text[eSysAdminName] = _(
125 "Please enter the name of the Citadel user account that should be granted "
126 "administrative privileges once created. If using internal authentication "
127 "this user account will be created if it does not exist. For external "
128 "authentication this user account has to exist.");
130 setup_titles[eSysAdminPW] = _("Administrator password:");
131 setup_text[eSysAdminPW] = _(
132 "Enter a password for the system administrator. When setup\n"
133 "completes it will attempt to create the administrator user\n"
134 "and set the password specified here.\n");
136 setup_titles[eUID] = _("Citadel User ID:");
137 setup_text[eUID] = _(
138 "Citadel needs to run under its own user ID. This would\n"
139 "typically be called \"citadel\", but if you are running Citadel\n"
140 "as a public site, you might also call it \"bbs\" or \"guest\".\n"
141 "The server will run under this user ID. Please specify that\n"
142 "user ID here. You may specify either a user name or a numeric\n"
145 setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
146 setup_text[eIP_ADDR] = _(
147 "Please specify the IP address which the server should be listening to. "
148 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
149 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
150 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
151 "listen on all addresses. "
152 "This can usually be left to the default unless multiple instances of Citadel "
153 "are running on the same computer.");
155 setup_titles[eCTDL_Port] = _("Server port number:");
156 setup_text[eCTDL_Port] = _(
157 "Specify the TCP port number on which your server will run.\n"
158 "Normally, this will be port 504, which is the official port\n"
159 "assigned by the IANA for Citadel servers. You will only need\n"
160 "to specify a different port number if you run multiple instances\n"
161 "of Citadel on the same computer and there is something else\n"
162 "already using port 504.\n");
164 setup_titles[eAuthType] = _("Authentication method to use:");
165 setup_text[eAuthType] = _(
166 "Please choose the user authentication mode. By default Citadel will use its "
167 "own internal user accounts database. If you choose Host, Citadel users will "
168 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
169 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
170 "chooses the nonstandard MS Active Directory LDAP scheme."
172 "Do not change this option unless you are sure it is required, since changing "
173 "back requires a full reinstall of Citadel."
175 " 0. Self contained authentication\n"
176 " 1. Host system integrated authentication\n"
177 " 2. External LDAP - RFC 2307 POSIX schema\n"
178 " 3. External LDAP - MS Active Directory schema\n"
180 "For help: http://www.citadel.org/authmodes.html\n"
182 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
184 setup_titles[eLDAP_Host] = _("LDAP host:");
185 setup_text[eLDAP_Host] = _(
186 "Please enter the host name or IP address of your LDAP server.\n");
188 setup_titles[eLDAP_Port] = _("LDAP port number:");
189 setup_text[eLDAP_Port] = _(
190 "Please enter the port number of the LDAP service (usually 389).\n");
192 setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
193 setup_text[eLDAP_Base_DN] = _(
194 "Please enter the Base DN to search for authentication\n"
195 "(for example: dc=example,dc=com)\n");
197 setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
198 setup_text[eLDAP_Bind_DN] = _(
199 "Please enter the DN of an account to use for binding to the LDAP server for "
200 "performing queries. The account does not require any other privileges. If "
201 "your LDAP server allows anonymous queries, you can leave this blank.\n");
203 setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
204 setup_text[eLDAP_Bind_PW] = _(
205 "If you entered a Bind DN in the previous question, you must now enter\n"
206 "the password associated with that account. Otherwise, you can leave this\n"
212 printf("\033[2J\033[H\033[44m\033[1m\033[K\n");
213 printf(" %s \033[K\n", program_title);
219 void title(const char *text) {
221 printf("\033[1m\033[32m<\033[33m%s\033[32m>\033[0m\n", text);
225 int yesno(const char *question, int default_value) {
230 printf("\033[31m\033[32m%s\n%s [\033[33m%s\033[32m]\033[0m --> ", question, _("Yes/No"), ( default_value ? _("Yes") : _("No") ));
231 if (fgets(buf, sizeof buf, stdin)) {
232 answer = tolower(buf[0]);
233 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10)) {
234 answer = default_value;
236 else if (answer == 'y') {
239 else if (answer == 'n') {
243 } while ((answer < 0) || (answer > 1));
248 void important_message(const char *title, const char *msgtext) {
252 printf("%s\n%s\n\n", title, msgtext);
253 printf("%s", _("Press return to continue..."));
254 if (fgets(buf, sizeof buf, stdin)) {
260 void important_msgnum(int msgnum) {
261 important_message(_("Important Message"), setup_text[msgnum]);
265 void display_error(char *error_message_format, ...) {
270 va_start(arg_ptr, error_message_format);
271 StrBufVAppendPrintf(Msg, error_message_format, arg_ptr);
274 important_message(_("Error"), ChrPtr(Msg));
279 void progress(char *text, long int curr, long int cmax) {
285 printf("%s\n", text);
286 printf("\033[1m\033[33m[\033[32m............................................................................\033[33m]\033[0m\r");
288 else if (curr == cmax) {
289 printf("\r%79s\n", "");
292 printf("\033[1m\033[33m[\033[32m");
293 a = (curr * 100) / cmax;
296 for (i=0; i<a; ++i) {
305 int uds_connectsock(char *sockpath) {
307 struct sockaddr_un addr;
309 memset(&addr, 0, sizeof(addr));
310 addr.sun_family = AF_UNIX;
311 strcpy(addr.sun_path, sockpath);
313 s = socket(AF_UNIX, SOCK_STREAM, 0);
318 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
327 // input binary data from socket
328 void serv_read(char *buf, int bytes) {
332 while (len < bytes) {
333 rlen = read(serv_sock, &buf[len], bytes - len);
342 // send binary to server
343 void serv_write(char *buf, int nbytes) {
344 int bytes_written = 0;
346 while (bytes_written < nbytes) {
347 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
351 bytes_written = bytes_written + retval;
356 // input string from socket - implemented in terms of serv_read()
357 void serv_gets(char *buf) {
360 // Read one character at a time.
362 serv_read(&buf[i], 1);
363 if (buf[i] == '\n' || i == (SIZ-1))
367 // If we got a long line, discard characters until the newline.
369 while (buf[i] != '\n') {
370 serv_read(&buf[i], 1);
374 // Strip all trailing nonprintables (crlf)
379 // send line to server - implemented in terms of serv_write()
380 void serv_puts(char *buf) {
381 serv_write(buf, strlen(buf));
386 // Convenience functions to get/set system configuration entries
387 void getconf_str(char *buf, char *key) {
391 sprintf(cmd, "CONF GETVAL|%s", key);
395 extract_token(buf, &ret[4], 0, '|', SIZ);
403 int getconf_int(char *key) {
405 getconf_str(buf, key);
410 void setconf_str(char *key, char *val) {
413 sprintf(buf, "CONF PUTVAL|%s|%s", key, val);
419 void setconf_int(char *key, int val) {
422 sprintf(buf, "CONF PUTVAL|%s|%d", key, val);
428 // On systems which use xinetd, see if we can offer to install Citadel as
429 // the default telnet target.
430 void check_xinetd_entry(void) {
431 char *filename = "/etc/xinetd.d/telnet";
434 int already_citadel = 0;
437 fp = fopen(filename, "r+");
438 if (fp == NULL) return; // Not there. Oh well...
440 while (fgets(buf, sizeof buf, fp) != NULL) {
441 if (strstr(buf, "/citadel") != NULL) {
446 if (already_citadel) return; // Already set up this way.
448 // Otherwise, prompt the user to create an entry.
449 if (getenv("CREATE_XINETD_ENTRY") != NULL) {
450 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
455 snprintf(buf, sizeof buf,
456 _("Setup can configure the \"xinetd\" service to automatically\n"
457 "connect incoming telnet sessions to Citadel, bypassing the\n"
458 "host system login: prompt. Would you like to do this?\n"
461 if (yesno(buf, 1) == 0) {
466 fp = fopen(filename, "w");
468 "# description: telnet service for Citadel users\n"
473 " socket_type = stream\n"
476 " server = /usr/sbin/in.telnetd\n"
477 " server_args = -h -L %s/citadel\n"
478 " log_on_failure += USERID\n"
484 // Now try to restart the service. (This only works on systemd; others will need to restart it manually.)
485 rv = system("systemctl restart xinetd >/dev/null 2>&1");
487 rv = system("service xinetd restart >/dev/null 2>&1");
490 display_error(_("failed to restart xinetd.\n"));
495 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue) {
499 strcpy(setupmsg, "");
502 printf("\n%s\n", prompt_text);
503 printf("%s\n%s\n", _("This is currently set to:"), Target);
504 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
505 if (fgets(buf, sizeof buf, stdin)) {
506 buf[strlen(buf) - 1] = 0;
508 if (!IsEmptyStr(buf)) {
514 void set_bool_val(int msgpos, int *ip, char *DefValue) {
515 title(setup_titles[msgpos]);
516 *ip = yesno(setup_text[msgpos], *ip);
520 void set_str_val(int msgpos, char *Target, char *DefValue) {
521 strprompt(setup_titles[msgpos],
529 // like set_str_val() but for numeric values
530 void set_int_val(int msgpos, int *target, char *default_value) {
532 sprintf(buf, "%d", *target);
534 set_str_val(msgpos, buf, default_value);
535 } while ( (strcmp(buf, "0")) && (atoi(buf) == 0) );
540 void edit_value(int curr) {
541 struct passwd *pw = NULL;
542 char ctdluidname[256];
544 char *default_value = NULL;
550 if (default_value == NULL) {
557 getconf_str(admin_name, "c_sysadm");
558 set_str_val(curr, admin_name, default_value);
559 setconf_str("c_sysadm", admin_name);
563 set_str_val(curr, admin_pass, default_value);
567 ctdluid = getconf_int("c_ctdluid");
568 pw = getpwuid(ctdluid);
570 set_int_val(curr, &ctdluid, default_value);
573 strcpy(ctdluidname, pw->pw_name);
574 set_str_val(curr, ctdluidname, default_value);
575 pw = getpwnam(ctdluidname);
577 ctdluid = pw->pw_uid;
579 else if (atoi(ctdluidname) > 0) {
580 ctdluid = atoi(ctdluidname);
583 setconf_int("c_ctdluid", ctdluid);
587 getconf_str(buf, "c_ip_addr");
588 set_str_val(curr, buf, default_value);
589 setconf_str("c_ip_addr", buf);
593 portnum = getconf_int("c_port_number");
594 set_int_val(curr, &portnum, default_value);
595 setconf_int("c_port_number", portnum);
599 auth = getconf_int("c_auth_mode");
600 set_int_val(curr, &auth, default_value);
601 setconf_int("c_auth_mode", auth);
605 getconf_str(buf, "c_ldap_host");
606 if (IsEmptyStr(buf)) {
607 strcpy(buf, "localhost");
609 set_str_val(curr, buf, default_value);
610 setconf_str("c_ldap_host", buf);
614 lportnum = getconf_int("c_ldap_port");
618 set_int_val(curr, &lportnum, default_value);
619 setconf_int("c_ldap_port", lportnum);
623 getconf_str(buf, "c_ldap_base_dn");
624 set_str_val(curr, buf, default_value);
625 setconf_str("c_ldap_base_dn", buf);
629 getconf_str(buf, "c_ldap_bind_dn");
630 set_str_val(curr, buf, default_value);
631 setconf_str("c_ldap_bind_dn", buf);
635 getconf_str(buf, "c_ldap_bind_pw");
636 set_str_val(curr, buf, default_value);
637 setconf_str("c_ldap_bind_pw", buf);
643 // Messages that are no longer in use.
644 // We keep them here so we don't lose the translations if we need them later.
645 void unused_messages(void) {
646 important_message(_("Setup finished"),
647 _("Setup of the Citadel server is complete.\n"
648 "If you will be using WebCit, please run its\n"
649 "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")
656 important_message(_("Setup finished"),
657 _("Setup is finished. You may now start the server.")
662 int main(int argc, char *argv[]) {
667 char ctdldir[PATH_MAX]=CTDLDIR;
670 char *activity = NULL;
672 // Keep a mild groove on
673 program_title = _("Citadel Server setup");
675 // set an invalid setup type
678 // parse command line args
679 for (a = 0; a < argc; ++a) {
680 if (!strncmp(argv[a], "-u", 2)) {
681 strcpy(aaa, argv[a]);
682 strcpy(aaa, &aaa[2]);
683 setup_type = atoi(aaa);
685 else if (!strncmp(argv[a], "-h", 2)) {
686 safestrncpy(ctdldir, &argv[a][2], sizeof ctdldir);
690 if (chdir(ctdldir) != 0) {
691 fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
697 // Connect to the running Citadel server.
698 char *connectingmsg = _("Connecting to Citadel server");
699 for (i=0; ((i<30) && (serv_sock < 0)) ; ++i) { // wait for server to start up
700 progress(connectingmsg, i, 30);
701 serv_sock = uds_connectsock(file_citadel_admin_socket);
704 progress(connectingmsg, 30, 30);
709 _("Setup could not connect to a running Citadel server."),
710 strerror(errno), file_citadel_admin_socket
715 // read the server greeting
718 display_error("%s\n", buf);
722 // Are we connected to the correct Citadel server?
726 display_error("%s\n", buf);
730 while (serv_gets(buf), strcmp(buf, "000")) {
732 if (atoi(buf) != REV_LEVEL) {
733 display_error("%s\n", _("Your setup program and Citadel server are from different versions."));
740 printf("\n\n\n *** %s ***\n\n", program_title);
742 // Go through a series of dialogs prompting for config info
743 for (curr = 1; curr < eMaxQuestions; ++curr) {
746 if ( (curr == eAuthType)
747 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP)
748 && (getconf_int("c_auth_mode") != AUTHMODE_LDAP_AD)
750 curr += 5; // skip LDAP questions if we're not authenticating against LDAP
753 if (curr == eSysAdminName) {
754 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) { // for native auth mode, fetch the admin's existing pw
755 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
759 extract_token(admin_pass, &buf[4], 1, '|', sizeof admin_pass);
763 ++curr; // skip the password question for non-native auth modes
768 if ((pw = getpwuid( getconf_int("c_ctdluid") )) == NULL) {
775 // setup now must be run after Citadel Server is already running, so we don't need this anymore.
776 //if (create_run_directories(getconf_int("c_ctdluid"), gid) != 0) {
777 //display_error("%s\n", _("failed to create directories"));
780 activity = _("Reconfiguring Citadel server");
781 progress(activity, 0, 5);
782 sleep(1); // Let the message appear briefly
784 // Create the administrator account. It's ok if the command fails if this user already exists.
785 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
786 progress(activity, 1, 5);
787 snprintf(buf, sizeof buf, "CREU %s|%s", admin_name, admin_pass);
789 progress(activity, 2, 5);
792 progress(activity, 3, 5);
794 // Assign the desired password and access level to the administrator account.
795 if (getconf_int("c_auth_mode") == AUTHMODE_NATIVE) {
796 snprintf(buf, sizeof buf, "AGUP %s", admin_name);
798 progress(activity, 4, 5);
801 int admin_flags = extract_int(&buf[4], 2);
802 int admin_times_called = extract_int(&buf[4], 3);
803 int admin_msgs_posted = extract_int(&buf[4], 4);
804 snprintf(buf, sizeof buf, "ASUP %s|%s|%d|%d|%d|6",
805 admin_name, admin_pass, admin_flags, admin_times_called, admin_msgs_posted
811 progress(activity, 5, 5);
813 check_xinetd_entry(); // Check /etc/xinetd.d/telnet
816 activity = _("Restarting Citadel server to apply changes");
817 progress(activity, 0, 51);
821 long original_start_time = extract_long(&buf[4], 3);
823 progress(activity, 1, 51);
825 progress(activity, 2, 51);
828 display_error("%s\n", buf);
835 for (i=3; i<=6; ++i) { // wait for server to shut down
836 progress(activity, i, 51);
840 for (i=7; ((i<=48) && (serv_sock < 0)) ; ++i) { // wait for server to start up
841 progress(activity, i, 51);
842 serv_sock = uds_connectsock(file_citadel_admin_socket);
846 progress(activity, 49, 51);
849 progress(activity, 50, 51);
852 long new_start_time = extract_long(&buf[4], 3);
855 progress(activity, 51, 51);
857 if ((original_start_time == new_start_time) || (new_start_time <= 0)) {
858 display_error("%s\n", _("Setup failed to restart Citadel server. Please restart it manually."));