3 * Server for user registration with Moira and Kerberos.
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
12 #include <mr_private.h>
13 #include <moira_schema.h>
14 #include <moira_site.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
21 #include <sys/utsname.h>
23 #include <netinet/in.h>
40 EXEC SQL INCLUDE sqlca;
44 char *whoami, *hostname, *shorthostname;
46 char *find_usernames(char *first, char *middle, char *last);
47 int check_username_available(char *username);
48 void fixname(char *name);
49 int register_user(int uid, char *username);
50 int update_user_status(char *username, int account_status);
51 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
54 reg_client *cl = NULL;
55 enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
57 int main(int argc, char **argv)
59 int listener, nfds, i, clientid = 0;
60 fd_set readfds, xreadfds;
62 int nclients, clientssize;
70 whoami = strrchr(argv[0], '/');
71 whoami = whoami ? whoami + 1 : argv[0];
73 set_com_err_hook(mr_com_err);
78 com_err(whoami, errno, "reading RSA key");
83 com_err(whoami, errno, "reading HMAC key");
87 /* Read error messages */
90 com_err(whoami, errno, "reading error messages");
94 /* Connect to database */
95 EXEC SQL CONNECT :db IDENTIFIED BY :db;
99 int bufsize = 256, msglength = 0;
101 sqlglm(err_msg, &bufsize, &msglength);
102 err_msg[msglength] = 0;
103 com_err(whoami, 0, "SQL error connecting to DBMS:\n%s", err_msg);
107 /* Get my hostname */
109 h = gethostbyname(uts.nodename);
112 com_err(whoami, 0, "Couldn't resolve hostname %s", uts.nodename);
115 hostname = lowercase(xstrdup(h->h_name));
116 shorthostname = xstrdup(hostname);
117 if (strchr(shorthostname, '.'))
118 *strchr(shorthostname, '.') = '\0';
120 /* Initialize kerberos */
121 status = init_kerberos();
124 com_err(whoami, status, "initializing kerberos library");
128 /* Set up listening socket. */
129 listener = mr_listen("moira_ureg");
132 com_err(whoami, errno, "couldn't create listening socket");
136 FD_SET(listener, &xreadfds);
139 /* Initialize client array. */
142 clients = malloc(clientssize * sizeof(reg_client));
145 com_err(whoami, errno, "creating client array");
149 /* Set up signal handlers */
151 sigemptyset(&sa.sa_mask);
152 sa.sa_handler = sigshut;
153 sigaction(SIGTERM, &sa, NULL);
154 sigaction(SIGINT, &sa, NULL);
155 sigaction(SIGHUP, &sa, NULL);
156 sa.sa_handler = SIG_IGN;
157 sigaction(SIGPIPE, &sa, NULL);
159 com_err(whoami, 0, "started (pid %d)", getpid());
160 com_err(whoami, 0, rcsid);
163 while (state != RS_EXITING)
165 if (state == RS_RUNNING && stat(MOIRA_MOTD_FILE, &st) == 0)
168 com_err(whoami, 0, "found motd. reg_svr is sleeping");
170 else if (state == RS_SLEEPING && stat(MOIRA_MOTD_FILE, &st) == -1)
173 com_err(whoami, 0, "motd gone. reg_svr is running");
176 memcpy(&readfds, &xreadfds, sizeof(readfds));
177 if (select(nfds, &readfds, NULL, NULL, NULL) == -1)
180 com_err(whoami, errno, "in select");
184 if (FD_ISSET(listener, &readfds))
186 int newconn, addrlen = sizeof(struct sockaddr_in);
187 struct sockaddr_in addr;
189 newconn = accept(listener, (struct sockaddr *)&addr, &addrlen);
191 com_err(whoami, errno, "accepting new connection");
195 if (nclients > clientssize)
197 clientssize = 2 * clientssize;
198 clients = xrealloc(clients, clientssize *
202 cl = &clients[nclients - 1];
203 memset(cl, 0, sizeof(reg_client));
205 cl->lastmod = time(NULL);
206 cl->clientid = ++clientid;
207 cl->random = init_rand(cl);
208 FD_SET(newconn, &xreadfds);
213 "New connection from %s port %d (now %d client%s)",
214 inet_ntoa(addr.sin_addr), (int)ntohs(addr.sin_port),
215 nclients, nclients != 1 ? "s" : "");
219 for (i = 0; i < nclients; i++)
222 if (FD_ISSET(cl->fd, &readfds))
224 cl->lastmod = time(NULL);
227 /* We're just starting */
231 com_err(whoami, errno, "allocating read buffer");
232 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
241 /* We haven't read the length byte yet... */
242 cl->nread += read(cl->fd, cl->buf + cl->nread,
246 cl->nmax = cl->buf[1] * 256 + cl->buf[2] + 3;
247 cl->buf = realloc(cl->buf, cl->nmax + 3);
250 com_err(whoami, errno, "reallocating read buffer");
251 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
256 else if (cl->nread == 0)
258 /* client has closed connection. setting
259 lastmod will cause it to be reaped */
265 /* We know how long the packet is supposed to be */
266 cl->nread += read(cl->fd, cl->buf + cl->nread,
267 cl->nmax - cl->nread);
268 if (cl->nread == cl->nmax)
270 parse_packet(cl, cl->buf[0], cl->nread - 3, cl->buf + 3,
271 state == RS_SLEEPING);
279 if (cl->lastmod < time(NULL) - TIMEOUT)
281 com_err(whoami, 0, "Closed connection. (now %d client%s)",
282 nclients - 1, nclients != 2 ? "s" : "");
285 FD_CLR(cl->fd, &xreadfds);
289 free(cl->suggestions);
291 clients[i] = clients[--nclients];
297 com_err(whoami, 0, "Exiting.");
300 void RIFO(reg_client *rc, int argc, char **argv)
302 EXEC SQL BEGIN DECLARE SECTION;
303 char *ufirst, *umiddle, *ulast, *id;
304 char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE];
305 char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE];
306 char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE];
307 char class[USERS_TYPE_SIZE], pin[USERS_PIN_SIZE];
308 int uid, status, secure, sqlstatus, count;
309 EXEC SQL END DECLARE SECTION;
311 if (rc->uid || argc != 4)
313 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
322 EXEC SQL SELECT count(login) INTO :count FROM users WHERE clearid = :id;
324 /* "ORDER BY status" so that if there's both a matching state 0 entry
325 and a matching state 3 entry, we'll get the former. */
326 EXEC SQL DECLARE csr_id CURSOR FOR
327 SELECT login, unix_uid, status, secure, pin, first, middle, last, type
328 FROM users WHERE clearid = :id ORDER BY status;
329 EXEC SQL OPEN csr_id;
332 EXEC SQL FETCH csr_id INTO :login, :uid, :status,
333 :secure, :pin, :first, :middle, :last, :class;
343 /* It's possible they have both a deleted account and a status 8
344 * account. We can't compensate for that in the ORDER BY clause
345 * above, so check here. If they have more than one entry and the
346 * first one we get is deleted, skip it.
348 if (status == US_DELETED && count > 1)
351 /* Check names, allowing for the possibility that Moira and the
352 user might have them split up differently. eg, Mary/Ann/Singleton
353 vs. Mary Ann/Singleton. */
354 if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) &&
355 strncasecmp(last, ulast, strlen(ulast)))
357 if (strlen(last) > 3 && strlen(ulast) < 3)
359 if (strcasecmp(first, ufirst) &&
360 strncasecmp(first, ufirst, strlen(first)) &&
361 strncasecmp(first, ufirst, strlen(ufirst)))
363 if (strlen(first) > 3 && strlen(ufirst) < 3)
365 if (!*ufirst && !*ulast)
368 /* Ignore the middle name since Moira doesn't have those reliably */
371 sqlstatus = sqlca.sqlcode;
372 EXEC SQL CLOSE csr_id;
376 reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL);
384 case US_ENROLL_NOT_ALLOWED:
385 case US_REGISTERED_KERBEROS_ONLY:
386 reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
390 reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
394 reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
401 rc->user_status = status;
403 sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
404 if (!strcmp(class, "MITS"))
405 strcpy(class, "STAFF");
411 com_err(whoami, errno, "in RIFO");
412 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
418 rc->reserved_username = 1;
419 rc->username = strdup(login);
422 com_err(whoami, errno, "in RIFO");
423 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
429 rc->suggestions = find_usernames(first, middle, last);
430 if (!rc->suggestions && errno)
432 com_err(whoami, errno, "in RIFO");
433 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
441 reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
443 reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
445 else if (!rc->username)
446 reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
449 if (rc->user_status == US_NO_LOGIN_YET ||
450 rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
452 status = check_kerberos(login);
453 if (status == MR_SUCCESS &&
454 rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
455 status = register_user(rc->uid, login);
456 if (status == MR_IN_USE)
458 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
462 else if (status == MR_DOWN)
464 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
467 else if (status != MR_SUCCESS)
469 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
470 error_message(status));
474 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
478 void SWRD(reg_client *rc, int argc, char **argv)
483 if (!rc->id || argc != 6)
485 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
489 getwordlist(rc->id, words);
490 for (i = 0; i < 6; i++)
492 if (strcasecmp(strtrim(argv[i]), words[i]))
497 reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
504 reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
506 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
509 void SPIN(reg_client *rc, int argc, char **argv)
511 EXEC SQL BEGIN DECLARE SECTION;
512 char pin[USERS_PIN_SIZE];
513 EXEC SQL END DECLARE SECTION;
515 if (!rc->id || argc != 1)
517 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
521 EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
522 AND status = :rc->user_status;
524 if (strcmp(argv[0], pin) != 0)
526 reply(rc, BAD_PIN, "GETI", "d", NULL);
533 reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
536 register_user(rc->uid, rc->username);
537 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
541 void CLGN(reg_client *rc, int argc, char **argv)
547 if (!rc->uid || rc->id || rc->username || argc != 1)
549 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
555 /* make sure someone's not trying to overrun reply */
556 if (strlen(login) > 100)
558 com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
563 if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
564 (login[0] == '_') || isdigit(login[0]))
566 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
567 3, USERS_LOGIN_SIZE - 1);
571 for (i = 0; i < strlen(login); i++)
573 if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
575 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
576 3, USERS_LOGIN_SIZE - 1);
581 status = check_kerberos(login);
582 if (status == MR_SUCCESS)
584 status = check_username_available(login);
585 if (status == MR_SUCCESS)
587 reply(rc, USERNAME_AVAILABLE, "LOGC", "c", login, login);
592 if (status == MR_IN_USE)
594 if (rc->reserved_username)
596 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
600 reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
603 else if (status == MR_DOWN)
605 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
608 else if (status != MR_SUCCESS)
610 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
615 void LOGN(reg_client *rc, int argc, char **argv)
621 if (!rc->uid || rc->id || rc->username || argc != 1)
623 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
629 /* make sure someone's not trying to overrun reply */
630 if (strlen(login) > 100)
632 com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
637 if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
638 (login[0] == '_') || isdigit(login[0]))
640 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
641 3, USERS_LOGIN_SIZE - 1);
645 for (i = 0; i < strlen(login); i++)
647 if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
649 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
650 3, USERS_LOGIN_SIZE - 1);
655 status = check_kerberos(login);
656 if (status == MR_SUCCESS)
658 if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
659 EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
661 status = register_user(rc->uid, login);
663 if (status == MR_IN_USE)
665 if (rc->reserved_username)
667 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
671 reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
674 else if (status == MR_DOWN)
676 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
679 else if (status != MR_SUCCESS)
681 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
685 rc->username = strdup(login);
688 com_err(whoami, errno, "in LOGN");
689 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
692 reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
696 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
697 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
698 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
699 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
700 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
701 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
702 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
703 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
704 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
705 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
706 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
707 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
708 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
709 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
710 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
711 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
714 void PSWD(reg_client *rc, int argc, char **argv)
717 char *password = argv[0], *p;
718 EXEC SQL BEGIN DECLARE SECTION;
719 char *login = rc->username;
720 char potype[USERS_POTYPE_SIZE];
721 EXEC SQL END DECLARE SECTION;
723 if (!rc->username || rc->id || argc != 1)
725 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
729 /* password quality checking */
730 if (strlen(password) < 4)
732 reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
736 if (strlen(password) < 7)
738 for (p = password + 1; *p; p++)
740 if (ctypes[*p] != ctypes[*(p - 1)])
745 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
750 if (!strcasecmp(password, "GykoR-66") ||
751 !strcasecmp(password, "slaRooBey") ||
752 !strcasecmp(password, "krang-its") ||
753 !strcasecmp(password, "2HotPeetzas") ||
754 !strcasecmp(password, "ItzAGurl"))
756 reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
760 status = register_kerberos(rc->username, password);
761 if (status == MR_QUALITY)
763 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
766 else if (status == MR_IN_USE)
768 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
774 com_err(whoami, status, "registering username with Kerberos");
775 reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
779 if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
780 status = update_user_status(rc->username, US_REGISTERED_KERBEROS_ONLY);
782 status = update_user_status(rc->username, US_REGISTERED);
786 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
787 error_message(status));
791 EXEC SQL SELECT potype INTO :potype FROM users WHERE login = :login;
792 if (!strcmp(potype, "EXCHANGE"))
793 reply(rc, DONE, "INIT", "c", NULL, rc->username, "http://owa.mit.edu");
795 reply(rc, DONE, "INIT", "c", NULL, rc->username, "http://webmail.mit.edu");
798 void QUIT(reg_client *rc, int argc, char **argv)
802 /* Update a user's status in Moira */
803 int update_user_status(char *username, int account_status)
805 char statusbuf[2], *qargv[2], *motd = NULL;
808 status = mr_connect(hostname);
812 status = mr_motd(&motd);
819 status = mr_krb5_auth("reg_svr");
822 com_err(whoami, status, "authenticating to moira");
827 sprintf(statusbuf, "%d", account_status);
829 qargv[1] = statusbuf;
831 status = mr_query("update_user_status", 2, qargv, NULL, NULL);
836 /* Register a user in Moira */
837 int register_user(int uid, char *username)
839 EXEC SQL BEGIN DECLARE SECTION;
840 char class[USERS_TYPE_SIZE];
841 EXEC SQL END DECLARE SECTION;
842 char uidbuf[10], *qargv[3], *motd = NULL;
845 status = mr_connect(hostname);
849 status = mr_motd(&motd);
856 status = mr_krb5_auth("reg_svr");
859 com_err(whoami, status, "authenticating to moira");
864 EXEC SQL SELECT type INTO :class FROM users WHERE unix_uid = :uid;
866 sprintf(uidbuf, "%d", uid);
870 /* Incoming students should be given Exchange poboxes.
871 * Doesn't work for undergrads in the class of 2100 or higher.
873 if (!strcmp(strtrim(class), "G") || !strncmp(class, "FALL", 4) ||
874 !strncmp(class, "SPRING", 5) || !strncmp(class, "SUMMER", 6) ||
875 !strncmp(class, "20", 2))
877 com_err(whoami, 0, "assigning EXCHANGE pobox to user %s, class %s", username, class);
878 qargv[2] = "EXCHANGE";
882 com_err(whoami, 0, "assigning IMAP pobox to user %s, class %s", username, class);
886 status = mr_query("register_user", 3, qargv, NULL, NULL);
892 /* Find some typical available usernames */
894 char *uname_patterns[] = {
896 "fmllllll", /* jmdoe... (last name truncated) */
897 "flllllll", /* jdoe.... ("") */
898 "llllllll", /* doe..... ("") */
905 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
907 char *find_usernames(char *first, char *middle, char *last)
909 EXEC SQL BEGIN DECLARE SECTION;
910 char username[2 * USERS_LOGIN_SIZE];
912 EXEC SQL END DECLARE SECTION;
914 char *pp, *up, *fp, *mp, *lp, *unames = NULL;
920 for (pat = 0; pat < num_patterns; pat++)
926 for (pp = uname_patterns[pat]; *pp; pp++)
936 if (up - username + strlen(first) < USERS_LOGIN_SIZE)
937 up += sprintf(up, "%s", first);
955 if (up - username + strlen(last) < USERS_LOGIN_SIZE)
956 up += sprintf(up, "%s", last);
964 if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
967 EXEC SQL SELECT COUNT(login) INTO :count FROM users
968 WHERE login = :username;
976 EXEC SQL SELECT COUNT(name) INTO :count FROM list
977 WHERE name = :username;
986 EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
987 WHERE label = :username;
999 unames = realloc(unames, strlen(unames) + strlen(username) + 3);
1002 strcat(unames, ", ");
1003 strcat(unames, username);
1007 unames = strdup(username);
1017 /* unames will be NULL if we couldn't suggest a username. Clear
1018 errno so the caller can distinguish this from an error case. */
1023 /* This does the database-side checks to make sure a username is
1026 int check_username_available(char *username)
1030 EXEC SQL SELECT COUNT(login) INTO :count FROM users
1031 WHERE login = :username;
1037 EXEC SQL SELECT COUNT(name) INTO :count FROM list
1038 WHERE name = :username;
1044 EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
1045 WHERE label = :username;
1051 EXEC SQL SELECT COUNT(login) INTO :count FROM userhistory
1052 WHERE login = :username;
1061 void fixname(char *name)
1065 for (s = d = name; *s; s++)
1073 void *xmalloc(size_t bytes)
1075 void *buf = malloc(bytes);
1080 com_err(whoami, errno, "in xmalloc");
1084 void *xrealloc(void *ptr, size_t bytes)
1086 void *buf = realloc(ptr, bytes);
1091 com_err(whoami, errno, "in xrealloc");
1095 char *xstrdup(char *str)
1097 char *buf = strdup(str);
1102 com_err(whoami, errno, "in xstrdup");
1106 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1110 fputs(whoami, stderr);
1112 fprintf(stderr, "[#%d]", cl->clientid);
1113 fputs(": ", stderr);
1116 fputs(error_message(code), stderr);
1120 vfprintf(stderr, fmt, pvar);
1124 void sigshut(int sig)