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>
38 EXEC SQL INCLUDE sqlca;
42 char *whoami, *hostname, *shorthostname;
44 char *find_usernames(char *first, char *middle, char *last);
45 int check_username_available(char *username);
46 void fixname(char *name);
47 int register_user(int uid, char *username);
48 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
51 reg_client *cl = NULL;
52 enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
54 int main(int argc, char **argv)
56 int listener, nfds, i, clientid = 0;
57 fd_set readfds, xreadfds;
59 int nclients, clientssize;
67 whoami = strrchr(argv[0], '/');
68 whoami = whoami ? whoami + 1 : argv[0];
70 set_com_err_hook(mr_com_err);
75 com_err(whoami, errno, "reading RSA key");
80 com_err(whoami, errno, "reading HMAC key");
84 /* Read error messages */
87 com_err(whoami, errno, "reading error messages");
91 /* Connect to database */
92 EXEC SQL CONNECT :db IDENTIFIED BY :db;
96 int bufsize = 256, msglength = 0;
98 sqlglm(err_msg, &bufsize, &msglength);
99 err_msg[msglength] = 0;
100 com_err(whoami, 0, "SQL error connecting to DBMS:\n%s", err_msg);
104 /* Get my hostname */
106 h = gethostbyname(uts.nodename);
109 com_err(whoami, 0, "Couldn't resolve hostname %s", uts.nodename);
112 hostname = lowercase(xstrdup(h->h_name));
113 shorthostname = xstrdup(hostname);
114 if (strchr(shorthostname, '.'))
115 *strchr(shorthostname, '.') = '\0';
117 /* Initialize kerberos */
118 status = init_kerberos();
121 com_err(whoami, status, "initializing kerberos library");
125 /* Set up listening socket. */
126 listener = mr_listen("moira_ureg");
129 com_err(whoami, errno, "couldn't create listening socket");
133 FD_SET(listener, &xreadfds);
136 /* Initialize client array. */
139 clients = malloc(clientssize * sizeof(reg_client));
142 com_err(whoami, errno, "creating client array");
146 /* Set up signal handlers */
148 sigemptyset(&sa.sa_mask);
149 sa.sa_handler = sigshut;
150 sigaction(SIGTERM, &sa, NULL);
151 sigaction(SIGINT, &sa, NULL);
152 sigaction(SIGHUP, &sa, NULL);
153 sa.sa_handler = SIG_IGN;
154 sigaction(SIGPIPE, &sa, NULL);
156 com_err(whoami, 0, "started (pid %d)", getpid());
157 com_err(whoami, 0, rcsid);
160 while (state != RS_EXITING)
162 if (state == RS_RUNNING && stat(MOIRA_MOTD_FILE, &st) == 0)
165 com_err(whoami, 0, "found motd. reg_svr is sleeping");
167 else if (state == RS_SLEEPING && stat(MOIRA_MOTD_FILE, &st) == -1)
170 com_err(whoami, 0, "motd gone. reg_svr is running");
173 memcpy(&readfds, &xreadfds, sizeof(readfds));
174 if (select(nfds, &readfds, NULL, NULL, NULL) == -1)
177 com_err(whoami, errno, "in select");
181 if (FD_ISSET(listener, &readfds))
183 int newconn, addrlen = sizeof(struct sockaddr_in);
184 struct sockaddr_in addr;
186 newconn = accept(listener, (struct sockaddr *)&addr, &addrlen);
188 com_err(whoami, errno, "accepting new connection");
192 if (nclients > clientssize)
194 clientssize = 2 * clientssize;
195 clients = xrealloc(clients, clientssize *
199 cl = &clients[nclients - 1];
200 memset(cl, 0, sizeof(reg_client));
202 cl->lastmod = time(NULL);
203 cl->clientid = ++clientid;
204 cl->random = init_rand(cl);
205 FD_SET(newconn, &xreadfds);
210 "New connection from %s port %d (now %d client%s)",
211 inet_ntoa(addr.sin_addr), (int)ntohs(addr.sin_port),
212 nclients, nclients != 1 ? "s" : "");
216 for (i = 0; i < nclients; i++)
219 if (FD_ISSET(cl->fd, &readfds))
221 cl->lastmod = time(NULL);
224 /* We're just starting */
228 com_err(whoami, errno, "allocating read buffer");
229 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
238 /* We haven't read the length byte yet... */
239 cl->nread += read(cl->fd, cl->buf + cl->nread,
243 cl->nmax = cl->buf[1] * 256 + cl->buf[2] + 3;
244 cl->buf = realloc(cl->buf, cl->nmax + 3);
247 com_err(whoami, errno, "reallocating read buffer");
248 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
253 else if (cl->nread == 0)
255 /* client has closed connection. setting
256 lastmod will cause it to be reaped */
262 /* We know how long the packet is supposed to be */
263 cl->nread += read(cl->fd, cl->buf + cl->nread,
264 cl->nmax - cl->nread);
265 if (cl->nread == cl->nmax)
267 parse_packet(cl, cl->buf[0], cl->nread - 3, cl->buf + 3,
268 state == RS_SLEEPING);
276 if (cl->lastmod < time(NULL) - TIMEOUT)
278 com_err(whoami, 0, "Closed connection. (now %d client%s)",
279 nclients - 1, nclients != 2 ? "s" : "");
282 FD_CLR(cl->fd, &xreadfds);
286 free(cl->suggestions);
288 clients[i] = clients[--nclients];
294 com_err(whoami, 0, "Exiting.");
297 void RIFO(reg_client *rc, int argc, char **argv)
299 EXEC SQL BEGIN DECLARE SECTION;
300 char *ufirst, *umiddle, *ulast, *id;
301 char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE];
302 char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE];
303 char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE];
304 char class[USERS_TYPE_SIZE], pin[USERS_PIN_SIZE];
305 int uid, status, secure, sqlstatus, count;
306 EXEC SQL END DECLARE SECTION;
308 if (rc->uid || argc != 4)
310 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
319 EXEC SQL SELECT count(login) INTO :count FROM users WHERE clearid = :id;
321 /* "ORDER BY status" so that if there's both a matching state 0 entry
322 and a matching state 3 entry, we'll get the former. */
323 EXEC SQL DECLARE csr_id CURSOR FOR
324 SELECT login, unix_uid, status, secure, pin, first, middle, last, type
325 FROM users WHERE clearid = :id ORDER BY status;
326 EXEC SQL OPEN csr_id;
329 EXEC SQL FETCH csr_id INTO :login, :uid, :status,
330 :secure, :pin, :first, :middle, :last, :class;
340 /* It's possible they have both a deleted account and a status 8
341 * account. We can't compensate for that in the ORDER BY clause
342 * above, so check here. If they have more than one entry and the
343 * first one we get is deleted, skip it.
345 if (status == US_DELETED && count > 1)
348 /* Check names, allowing for the possibility that Moira and the
349 user might have them split up differently. eg, Mary/Ann/Singleton
350 vs. Mary Ann/Singleton. */
351 if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) &&
352 strncasecmp(last, ulast, strlen(ulast)))
354 if (strlen(last) > 3 && strlen(ulast) < 3)
356 if (strcasecmp(first, ufirst) &&
357 strncasecmp(first, ufirst, strlen(first)) &&
358 strncasecmp(first, ufirst, strlen(ufirst)))
360 if (strlen(first) > 3 && strlen(ufirst) < 3)
362 if (!*ufirst && !*ulast)
365 /* Ignore the middle name since Moira doesn't have those reliably */
368 sqlstatus = sqlca.sqlcode;
369 EXEC SQL CLOSE csr_id;
373 reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL);
381 case US_ENROLL_NOT_ALLOWED:
382 case US_REGISTERED_KERBEROS_ONLY:
383 reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
387 reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
391 reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
398 rc->user_status = status;
400 sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
401 if (!strcmp(class, "MITS"))
402 strcpy(class, "STAFF");
408 com_err(whoami, errno, "in RIFO");
409 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
415 rc->reserved_username = 1;
416 rc->username = strdup(login);
419 com_err(whoami, errno, "in RIFO");
420 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
426 rc->suggestions = find_usernames(first, middle, last);
427 if (!rc->suggestions && errno)
429 com_err(whoami, errno, "in RIFO");
430 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
438 reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
440 reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
442 else if (!rc->username)
443 reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
446 if (rc->user_status == US_NO_LOGIN_YET ||
447 rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
449 status = check_kerberos(login);
450 if (status == MR_SUCCESS &&
451 rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
452 status = register_user(rc->uid, login);
453 if (status == MR_IN_USE)
455 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
459 else if (status == MR_DOWN)
461 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
464 else if (status != MR_SUCCESS)
466 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
467 error_message(status));
471 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
475 void SWRD(reg_client *rc, int argc, char **argv)
480 if (!rc->id || argc != 6)
482 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
486 getwordlist(rc->id, words);
487 for (i = 0; i < 6; i++)
489 if (strcasecmp(strtrim(argv[i]), words[i]))
494 reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
501 reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
503 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
506 void SPIN(reg_client *rc, int argc, char **argv)
508 EXEC SQL BEGIN DECLARE SECTION;
509 char pin[USERS_PIN_SIZE];
510 EXEC SQL END DECLARE SECTION;
512 if (!rc->id || argc != 1)
514 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
518 EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
519 AND status = :rc->user_status;
521 if (strcmp(argv[0], pin) != 0)
523 reply(rc, BAD_PIN, "GETI", "d", NULL);
530 reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
533 register_user(rc->uid, rc->username);
534 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
538 void CLGN(reg_client *rc, int argc, char **argv)
544 if (!rc->uid || rc->id || rc->username || argc != 1)
546 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
552 /* make sure someone's not trying to overrun reply */
553 if (strlen(login) > 100)
555 com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
560 if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
561 (login[0] == '_') || isdigit(login[0]))
563 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
564 3, USERS_LOGIN_SIZE - 1);
568 for (i = 0; i < strlen(login); i++)
570 if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
572 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
573 3, USERS_LOGIN_SIZE - 1);
578 status = check_kerberos(login);
579 if (status == MR_SUCCESS)
581 status = check_username_available(login);
582 if (status == MR_SUCCESS)
584 reply(rc, USERNAME_AVAILABLE, "LOGC", "c", login, login);
589 if (status == MR_IN_USE)
591 if (rc->reserved_username)
593 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
597 reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
600 else if (status == MR_DOWN)
602 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
605 else if (status != MR_SUCCESS)
607 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
612 void LOGN(reg_client *rc, int argc, char **argv)
618 if (!rc->uid || rc->id || rc->username || argc != 1)
620 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
626 /* make sure someone's not trying to overrun reply */
627 if (strlen(login) > 100)
629 com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
634 if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
635 (login[0] == '_') || isdigit(login[0]))
637 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
638 3, USERS_LOGIN_SIZE - 1);
642 for (i = 0; i < strlen(login); i++)
644 if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
646 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
647 3, USERS_LOGIN_SIZE - 1);
652 status = check_kerberos(login);
653 if (status == MR_SUCCESS)
655 if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
656 EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
658 status = register_user(rc->uid, login);
660 if (status == MR_IN_USE)
662 if (rc->reserved_username)
664 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
668 reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
671 else if (status == MR_DOWN)
673 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
676 else if (status != MR_SUCCESS)
678 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
682 rc->username = strdup(login);
685 com_err(whoami, errno, "in LOGN");
686 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
689 reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
693 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
694 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
695 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
696 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
697 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
698 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
699 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
700 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
701 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
702 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
703 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
711 void PSWD(reg_client *rc, int argc, char **argv)
714 char *password = argv[0], *p;
715 EXEC SQL BEGIN DECLARE SECTION;
716 char *login = rc->username;
717 EXEC SQL END DECLARE SECTION;
719 if (!rc->username || rc->id || argc != 1)
721 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
725 /* password quality checking */
726 if (strlen(password) < 4)
728 reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
732 if (strlen(password) < 7)
734 for (p = password + 1; *p; p++)
736 if (ctypes[*p] != ctypes[*(p - 1)])
741 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
746 if (!strcasecmp(password, "GykoR-66") ||
747 !strcasecmp(password, "slaRooBey") ||
748 !strcasecmp(password, "krang-its") ||
749 !strcasecmp(password, "2HotPeetzas") ||
750 !strcasecmp(password, "ItzAGurl"))
752 reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
756 status = register_kerberos(rc->username, password);
757 if (status == MR_QUALITY)
759 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
762 else if (status == MR_IN_USE)
764 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
770 com_err(whoami, status, "registering username with Kerberos");
771 reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
775 if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
776 EXEC SQL UPDATE users SET status = 9 WHERE login = :login;
778 EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
781 reply(rc, DONE, "INIT", "c", NULL, rc->username);
784 void QUIT(reg_client *rc, int argc, char **argv)
788 /* Register a user in Moira */
789 int register_user(int uid, char *username)
791 EXEC SQL BEGIN DECLARE SECTION;
792 char class[USERS_TYPE_SIZE];
793 EXEC SQL END DECLARE SECTION;
794 char uidbuf[10], *qargv[3], *motd = NULL;
797 status = mr_connect(hostname);
801 status = mr_motd(&motd);
808 status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
809 krb_realmofhost(hostname), MOIRA_SNAME,
810 shorthostname, 3, KEYFILE);
812 status += ERROR_TABLE_BASE_krb;
814 status = mr_krb5_auth("reg_svr");
817 com_err(whoami, status, "authenticating to moira");
822 EXEC SQL SELECT type INTO :class FROM users WHERE unix_uid = :uid;
824 sprintf(uidbuf, "%d", uid);
828 /* Incoming students should be given Exchange poboxes.
829 * Doesn't work for undergrads in the class of 2100 or higher.
831 if (!strcmp(strtrim(class), "G") || !strncmp(class, "FALL", 4) ||
832 !strncmp(class, "SPRING", 5) || !strncmp(class, "SUMMER", 6) ||
833 !strncmp(class, "20", 2))
835 com_err(whoami, 0, "assigning EXCHANGE pobox to user %s, class %s", username, class);
836 qargv[2] = "EXCHANGE";
840 com_err(whoami, 0, "assigning IMAP pobox to user %s, class %s", username, class);
844 status = mr_query("register_user", 3, qargv, NULL, NULL);
850 /* Find some typical available usernames */
852 char *uname_patterns[] = {
854 "fmllllll", /* jmdoe... (last name truncated) */
855 "flllllll", /* jdoe.... ("") */
856 "llllllll", /* doe..... ("") */
863 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
865 char *find_usernames(char *first, char *middle, char *last)
867 EXEC SQL BEGIN DECLARE SECTION;
868 char username[2 * USERS_LOGIN_SIZE];
870 EXEC SQL END DECLARE SECTION;
872 char *pp, *up, *fp, *mp, *lp, *unames = NULL;
878 for (pat = 0; pat < num_patterns; pat++)
884 for (pp = uname_patterns[pat]; *pp; pp++)
894 if (up - username + strlen(first) < USERS_LOGIN_SIZE)
895 up += sprintf(up, "%s", first);
913 if (up - username + strlen(last) < USERS_LOGIN_SIZE)
914 up += sprintf(up, "%s", last);
922 if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
925 EXEC SQL SELECT COUNT(login) INTO :count FROM users
926 WHERE login = :username;
934 EXEC SQL SELECT COUNT(name) INTO :count FROM list
935 WHERE name = :username;
944 EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
945 WHERE label = :username;
957 unames = realloc(unames, strlen(unames) + strlen(username) + 3);
960 strcat(unames, ", ");
961 strcat(unames, username);
965 unames = strdup(username);
975 /* unames will be NULL if we couldn't suggest a username. Clear
976 errno so the caller can distinguish this from an error case. */
981 /* This does the database-side checks to make sure a username is
984 int check_username_available(char *username)
988 EXEC SQL SELECT COUNT(login) INTO :count FROM users
989 WHERE login = :username;
995 EXEC SQL SELECT COUNT(name) INTO :count FROM list
996 WHERE name = :username;
1002 EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
1003 WHERE label = :username;
1009 EXEC SQL SELECT COUNT(login) INTO :count FROM userhistory
1010 WHERE login = :username;
1019 void fixname(char *name)
1023 for (s = d = name; *s; s++)
1031 void *xmalloc(size_t bytes)
1033 void *buf = malloc(bytes);
1038 com_err(whoami, errno, "in xmalloc");
1042 void *xrealloc(void *ptr, size_t bytes)
1044 void *buf = realloc(ptr, bytes);
1049 com_err(whoami, errno, "in xrealloc");
1053 char *xstrdup(char *str)
1055 char *buf = strdup(str);
1060 com_err(whoami, errno, "in xstrdup");
1064 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1068 fputs(whoami, stderr);
1070 fprintf(stderr, "[#%d]", cl->clientid);
1071 fputs(": ", stderr);
1074 fputs(error_message(code), stderr);
1078 vfprintf(stderr, fmt, pvar);
1082 void sigshut(int sig)