122549Sdist /* 235392Sbostic * Copyright (c) 1983 The Regents of the University of California. 335392Sbostic * All rights reserved. 435392Sbostic * 542819Sbostic * %sccs.include.redist.c% 622549Sdist */ 722549Sdist 86460Swnj #ifndef lint 922549Sdist char copyright[] = 1035392Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ 1122549Sdist All rights reserved.\n"; 1235392Sbostic #endif /* not lint */ 136460Swnj 1422549Sdist #ifndef lint 15*57739Ssklower static char sccsid[] = "@(#)rwhod.c 5.22 (Berkeley) 01/29/93"; 1635392Sbostic #endif /* not lint */ 1722549Sdist 1842265Skarels #include <sys/param.h> 199215Ssam #include <sys/socket.h> 209215Ssam #include <sys/stat.h> 2137295Sbostic #include <sys/signal.h> 229215Ssam #include <sys/ioctl.h> 2352574Ssklower #include <sys/kinfo.h> 2413187Ssam #include <sys/file.h> 259215Ssam 2612236Ssam #include <net/if.h> 2752574Ssklower #include <net/if_dl.h> 2852574Ssklower #include <net/route.h> 299215Ssam #include <netinet/in.h> 309215Ssam 319215Ssam #include <nlist.h> 326460Swnj #include <errno.h> 336460Swnj #include <utmp.h> 348486Ssam #include <ctype.h> 358486Ssam #include <netdb.h> 3616673Sralph #include <syslog.h> 3723535Smckusick #include <protocols/rwhod.h> 3837295Sbostic #include <stdio.h> 39*57739Ssklower #include <unistd.h> 4037971Sbostic #include <paths.h> 416460Swnj 4215541Sralph /* 4315541Sralph * Alarm interval. Don't forget to change the down time check in ruptime 4415541Sralph * if this is changed. 4515541Sralph */ 4616673Sralph #define AL_INTERVAL (3 * 60) 4715541Sralph 4842265Skarels struct sockaddr_in sin; 496460Swnj 5042265Skarels char myname[MAXHOSTNAMELEN]; 516460Swnj 526460Swnj struct nlist nl[] = { 5338182Smckusick #define NL_BOOTTIME 0 549215Ssam { "_boottime" }, 556460Swnj 0 566460Swnj }; 576460Swnj 5812236Ssam /* 5912236Ssam * We communicate with each neighbor in 6012236Ssam * a list constructed at the time we're 6112236Ssam * started up. Neighbors are currently 6212236Ssam * directly connected via a hardware interface. 6312236Ssam */ 6412236Ssam struct neighbor { 6512236Ssam struct neighbor *n_next; 6612236Ssam char *n_name; /* interface name */ 6752574Ssklower struct sockaddr *n_addr; /* who to send to */ 6812236Ssam int n_addrlen; /* size of address */ 6912236Ssam int n_flags; /* should forward?, interface flags */ 7012236Ssam }; 7112236Ssam 7212236Ssam struct neighbor *neighbors; 736460Swnj struct whod mywd; 7412236Ssam struct servent *sp; 756460Swnj int s, utmpf, kmemf = -1; 766460Swnj 7712236Ssam #define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we)) 7811834Ssam 7937295Sbostic extern int errno; 8032449Sbostic char *strcpy(), *malloc(); 8146927Sbostic void getkmem(), onalrm(); 8212236Ssam struct in_addr inet_makeaddr(); 836460Swnj 846460Swnj main() 856460Swnj { 866460Swnj struct sockaddr_in from; 8726016Skarels struct stat st; 886460Swnj char path[64]; 8926484Smckusick int on = 1; 9037295Sbostic char *cp, *index(), *strerror(); 916460Swnj 9216673Sralph if (getuid()) { 9316673Sralph fprintf(stderr, "rwhod: not super user\n"); 9416673Sralph exit(1); 9516673Sralph } 968486Ssam sp = getservbyname("who", "udp"); 978486Ssam if (sp == 0) { 988486Ssam fprintf(stderr, "rwhod: udp/who: unknown service\n"); 998486Ssam exit(1); 1008486Ssam } 1016460Swnj #ifndef DEBUG 10244707Skarels daemon(1, 0); 1036460Swnj #endif 10437295Sbostic if (chdir(_PATH_RWHODIR) < 0) { 10537295Sbostic (void)fprintf(stderr, "rwhod: %s: %s\n", 10637295Sbostic _PATH_RWHODIR, strerror(errno)); 10726011Smckusick exit(1); 10826011Smckusick } 1096460Swnj (void) signal(SIGHUP, getkmem); 11024853Seric openlog("rwhod", LOG_PID, LOG_DAEMON); 11112236Ssam /* 11212236Ssam * Establish host name as returned by system. 11312236Ssam */ 11412236Ssam if (gethostname(myname, sizeof (myname) - 1) < 0) { 11516673Sralph syslog(LOG_ERR, "gethostname: %m"); 1166460Swnj exit(1); 1176460Swnj } 11824745Sbloom if ((cp = index(myname, '.')) != NULL) 11924745Sbloom *cp = '\0'; 12012236Ssam strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1); 12137295Sbostic utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); 1226460Swnj if (utmpf < 0) { 12337295Sbostic syslog(LOG_ERR, "%s: %m", _PATH_UTMP); 1246460Swnj exit(1); 1256460Swnj } 1266460Swnj getkmem(); 12713187Ssam if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 12816673Sralph syslog(LOG_ERR, "socket: %m"); 1299215Ssam exit(1); 1306460Swnj } 13117155Ssam if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { 13217055Skarels syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 13317055Skarels exit(1); 13417055Skarels } 13542265Skarels sin.sin_family = AF_INET; 13612236Ssam sin.sin_port = sp->s_port; 13746927Sbostic if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 13816673Sralph syslog(LOG_ERR, "bind: %m"); 1399215Ssam exit(1); 1409215Ssam } 14112236Ssam if (!configure(s)) 14212236Ssam exit(1); 14313027Ssam signal(SIGALRM, onalrm); 1446460Swnj onalrm(); 1456460Swnj for (;;) { 1466460Swnj struct whod wd; 14712236Ssam int cc, whod, len = sizeof (from); 1486460Swnj 14912236Ssam cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0, 15046927Sbostic (struct sockaddr *)&from, &len); 1516460Swnj if (cc <= 0) { 1526460Swnj if (cc < 0 && errno != EINTR) 15316673Sralph syslog(LOG_WARNING, "recv: %m"); 1546460Swnj continue; 1556460Swnj } 1568486Ssam if (from.sin_port != sp->s_port) { 15716673Sralph syslog(LOG_WARNING, "%d: bad from port", 1588486Ssam ntohs(from.sin_port)); 1596460Swnj continue; 1606460Swnj } 16112347Ssam if (wd.wd_vers != WHODVERSION) 16212347Ssam continue; 16312236Ssam if (wd.wd_type != WHODTYPE_STATUS) 16412236Ssam continue; 1658486Ssam if (!verify(wd.wd_hostname)) { 16616673Sralph syslog(LOG_WARNING, "malformed host name from %x", 1678486Ssam from.sin_addr); 1688486Ssam continue; 1698486Ssam } 17026011Smckusick (void) sprintf(path, "whod.%s", wd.wd_hostname); 17126016Skarels /* 17226016Skarels * Rather than truncating and growing the file each time, 17326016Skarels * use ftruncate if size is less than previous size. 17426016Skarels */ 17526016Skarels whod = open(path, O_WRONLY | O_CREAT, 0644); 1766460Swnj if (whod < 0) { 17716673Sralph syslog(LOG_WARNING, "%s: %m", path); 1786460Swnj continue; 1796460Swnj } 18042265Skarels #if ENDIAN != BIG_ENDIAN 18111834Ssam { 18213187Ssam int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 18311834Ssam struct whoent *we; 18411834Ssam 18511834Ssam /* undo header byte swapping before writing to file */ 18611834Ssam wd.wd_sendtime = ntohl(wd.wd_sendtime); 18711834Ssam for (i = 0; i < 3; i++) 18811834Ssam wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 18911834Ssam wd.wd_boottime = ntohl(wd.wd_boottime); 19011834Ssam we = wd.wd_we; 19111834Ssam for (i = 0; i < n; i++) { 19211834Ssam we->we_idle = ntohl(we->we_idle); 19312872Ssam we->we_utmp.out_time = 19412872Ssam ntohl(we->we_utmp.out_time); 19511834Ssam we++; 19611834Ssam } 19711834Ssam } 19811834Ssam #endif 19946927Sbostic (void) time((time_t *)&wd.wd_recvtime); 2006460Swnj (void) write(whod, (char *)&wd, cc); 20126016Skarels if (fstat(whod, &st) < 0 || st.st_size > cc) 20226016Skarels ftruncate(whod, cc); 2036460Swnj (void) close(whod); 2046460Swnj } 2056460Swnj } 2066460Swnj 2078486Ssam /* 2088486Ssam * Check out host name for unprintables 2098486Ssam * and other funnies before allowing a file 2108486Ssam * to be created. Sorry, but blanks aren't allowed. 2118486Ssam */ 2128486Ssam verify(name) 2138486Ssam register char *name; 2148486Ssam { 2158486Ssam register int size = 0; 2168486Ssam 2178486Ssam while (*name) { 21815214Sleres if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 2198486Ssam return (0); 2208486Ssam name++, size++; 2218486Ssam } 2228486Ssam return (size > 0); 2238486Ssam } 2248486Ssam 2256460Swnj int utmptime; 2266460Swnj int utmpent; 22722546Sbloom int utmpsize = 0; 22822546Sbloom struct utmp *utmp; 2296460Swnj int alarmcount; 2306460Swnj 23146927Sbostic void 2326460Swnj onalrm() 2336460Swnj { 23437295Sbostic register struct neighbor *np; 23537295Sbostic register struct whoent *we = mywd.wd_we, *wlast; 2366460Swnj register int i; 2376460Swnj struct stat stb; 2386460Swnj int cc; 2396460Swnj double avenrun[3]; 24037295Sbostic time_t now = time((time_t *)NULL); 24137295Sbostic char *strerror(); 2426460Swnj 2436460Swnj if (alarmcount % 10 == 0) 2446460Swnj getkmem(); 2456460Swnj alarmcount++; 2466460Swnj (void) fstat(utmpf, &stb); 24722546Sbloom if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) { 24817303Stef utmptime = stb.st_mtime; 24922546Sbloom if (stb.st_size > utmpsize) { 25022546Sbloom utmpsize = stb.st_size + 10 * sizeof(struct utmp); 25122546Sbloom if (utmp) 25222546Sbloom utmp = (struct utmp *)realloc(utmp, utmpsize); 25322546Sbloom else 25422546Sbloom utmp = (struct utmp *)malloc(utmpsize); 25522546Sbloom if (! utmp) { 25622546Sbloom fprintf(stderr, "rwhod: malloc failed\n"); 25722546Sbloom utmpsize = 0; 25822546Sbloom goto done; 25922546Sbloom } 26022546Sbloom } 261*57739Ssklower (void) lseek(utmpf, (off_t)0, L_SET); 26222546Sbloom cc = read(utmpf, (char *)utmp, stb.st_size); 2636460Swnj if (cc < 0) { 26437295Sbostic fprintf(stderr, "rwhod: %s: %s\n", 26537295Sbostic _PATH_UTMP, strerror(errno)); 26613469Ssam goto done; 2676460Swnj } 26812236Ssam wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1]; 2696460Swnj utmpent = cc / sizeof (struct utmp); 2706460Swnj for (i = 0; i < utmpent; i++) 2716460Swnj if (utmp[i].ut_name[0]) { 27212807Ssam bcopy(utmp[i].ut_line, we->we_utmp.out_line, 27312807Ssam sizeof (utmp[i].ut_line)); 27412807Ssam bcopy(utmp[i].ut_name, we->we_utmp.out_name, 27512807Ssam sizeof (utmp[i].ut_name)); 27612807Ssam we->we_utmp.out_time = htonl(utmp[i].ut_time); 2776577Ssam if (we >= wlast) 2786577Ssam break; 2796460Swnj we++; 2806460Swnj } 2816460Swnj utmpent = we - mywd.wd_we; 2826460Swnj } 28326484Smckusick 28426484Smckusick /* 28526484Smckusick * The test on utmpent looks silly---after all, if no one is 28626484Smckusick * logged on, why worry about efficiency?---but is useful on 28726484Smckusick * (e.g.) compute servers. 28826484Smckusick */ 28937971Sbostic if (utmpent && chdir(_PATH_DEV)) { 29037971Sbostic syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 29126484Smckusick exit(1); 29226484Smckusick } 2936460Swnj we = mywd.wd_we; 2946460Swnj for (i = 0; i < utmpent; i++) { 29526484Smckusick if (stat(we->we_utmp.out_line, &stb) >= 0) 29612807Ssam we->we_idle = htonl(now - stb.st_atime); 2976460Swnj we++; 2986460Swnj } 29938182Smckusick (void) getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 3006460Swnj for (i = 0; i < 3; i++) 30112873Ssam mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 3026460Swnj cc = (char *)we - (char *)&mywd; 30312807Ssam mywd.wd_sendtime = htonl(time(0)); 30412236Ssam mywd.wd_vers = WHODVERSION; 30512236Ssam mywd.wd_type = WHODTYPE_STATUS; 30612236Ssam for (np = neighbors; np != NULL; np = np->n_next) 30712236Ssam (void) sendto(s, (char *)&mywd, cc, 0, 30852574Ssklower np->n_addr, np->n_addrlen); 30937295Sbostic if (utmpent && chdir(_PATH_RWHODIR)) { 31037295Sbostic syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 31126484Smckusick exit(1); 31226484Smckusick } 31313469Ssam done: 31415541Sralph (void) alarm(AL_INTERVAL); 3156460Swnj } 3166460Swnj 31746927Sbostic void 3186460Swnj getkmem() 3196460Swnj { 32016663Ssam static ino_t vmunixino; 32116663Ssam static time_t vmunixctime; 32216663Ssam struct stat sb; 3236460Swnj 32437295Sbostic if (stat(_PATH_UNIX, &sb) < 0) { 32516663Ssam if (vmunixctime) 32616663Ssam return; 32716663Ssam } else { 32816663Ssam if (sb.st_ctime == vmunixctime && sb.st_ino == vmunixino) 32916663Ssam return; 33016663Ssam vmunixctime = sb.st_ctime; 33116663Ssam vmunixino= sb.st_ino; 33216663Ssam } 3336460Swnj if (kmemf >= 0) 3346460Swnj (void) close(kmemf); 3356460Swnj loop: 33637295Sbostic if (nlist(_PATH_UNIX, nl)) { 33737295Sbostic syslog(LOG_WARNING, "%s: namelist botch", _PATH_UNIX); 3386460Swnj sleep(300); 3396460Swnj goto loop; 3406460Swnj } 34137295Sbostic kmemf = open(_PATH_KMEM, O_RDONLY, 0); 3426460Swnj if (kmemf < 0) { 34337295Sbostic syslog(LOG_ERR, "%s: %m", _PATH_KMEM); 34416673Sralph exit(1); 3456460Swnj } 346*57739Ssklower (void) lseek(kmemf, (off_t)nl[NL_BOOTTIME].n_value, L_SET); 34713187Ssam (void) read(kmemf, (char *)&mywd.wd_boottime, 34813187Ssam sizeof (mywd.wd_boottime)); 34912807Ssam mywd.wd_boottime = htonl(mywd.wd_boottime); 3506460Swnj } 35112236Ssam 35252574Ssklower void 35352574Ssklower quit(msg) 35452574Ssklower char *msg; 35552574Ssklower { 35652574Ssklower syslog(LOG_ERR, msg); 35752574Ssklower exit(1); 35852574Ssklower } 35952574Ssklower 36052574Ssklower #define ROUNDUP(a) \ 36152574Ssklower ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 36252574Ssklower #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 36352574Ssklower 36452574Ssklower void 36552574Ssklower rt_xaddrs(cp, cplim, rtinfo) 36652574Ssklower register caddr_t cp, cplim; 36752574Ssklower register struct rt_addrinfo *rtinfo; 36852574Ssklower { 36952574Ssklower register struct sockaddr *sa; 37052574Ssklower register int i; 37152574Ssklower 37252574Ssklower bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info)); 37352574Ssklower for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 37452574Ssklower if ((rtinfo->rti_addrs & (1 << i)) == 0) 37552574Ssklower continue; 37652574Ssklower rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 37752574Ssklower ADVANCE(cp, sa); 37852574Ssklower } 37952574Ssklower } 38052574Ssklower 38112236Ssam /* 38212236Ssam * Figure out device configuration and select 38312236Ssam * networks which deserve status information. 38412236Ssam */ 38512236Ssam configure(s) 38612236Ssam int s; 38712236Ssam { 38852574Ssklower register struct neighbor *np; 38952574Ssklower register struct if_msghdr *ifm; 39052574Ssklower register struct ifa_msghdr *ifam; 39112236Ssam struct sockaddr_in *sin; 39252574Ssklower struct sockaddr_dl *sdl; 39352574Ssklower int needed, rlen = 0, flags = 0, len; 39452574Ssklower char *buf, *lim, *next; 39552574Ssklower struct rt_addrinfo info; 39612236Ssam 39752574Ssklower if ((needed = getkerninfo(KINFO_RT_IFLIST, 0, 0, 0)) < 0) 39852574Ssklower quit("route-getkerninfo-estimate"); 39952574Ssklower if ((buf = malloc(needed)) == NULL) 40052574Ssklower quit("malloc"); 40152574Ssklower if ((rlen = getkerninfo(KINFO_RT_IFLIST, buf, &needed, 0)) < 0) 40252574Ssklower quit("actual retrieval of interface table"); 40352574Ssklower lim = buf + rlen; 40452574Ssklower 40552574Ssklower for (next = buf; next < lim; next += ifm->ifm_msglen) { 40652574Ssklower ifm = (struct if_msghdr *)next; 40752574Ssklower if (ifm->ifm_type == RTM_IFINFO) { 40852574Ssklower sdl = (struct sockaddr_dl *)(ifm + 1); 40952574Ssklower flags = ifm->ifm_flags; 41052574Ssklower continue; 41152574Ssklower } 41252574Ssklower if ((flags & IFF_UP) == 0 || 41352574Ssklower (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 41452574Ssklower continue; 41552574Ssklower if (ifm->ifm_type != RTM_NEWADDR) 41652574Ssklower quit("out of sync parsing KINFO_RT_IFLIST"); 41752574Ssklower ifam = (struct ifa_msghdr *)ifm; 41852574Ssklower info.rti_addrs = ifam->ifam_addrs; 41952574Ssklower rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 42052574Ssklower &info); 42152574Ssklower /* gag, wish we could get rid of Internet dependencies */ 42252574Ssklower #define dstaddr info.rti_info[RTAX_BRD] 42352574Ssklower #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 42452574Ssklower #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 42552574Ssklower if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 42652574Ssklower continue; 42752574Ssklower PORT_SA(dstaddr) = sp->s_port; 42812236Ssam for (np = neighbors; np != NULL; np = np->n_next) 42952574Ssklower if (bcmp(sdl->sdl_data, np->n_name, sdl->sdl_nlen) == 0 43052574Ssklower && IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 43112236Ssam break; 43212236Ssam if (np != NULL) 43312236Ssam continue; 43452574Ssklower len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 43552574Ssklower np = (struct neighbor *)malloc(len); 43612236Ssam if (np == NULL) 43752574Ssklower quit("malloc of neighbor structure"); 43852574Ssklower bzero((char *)np, len); 43952574Ssklower np->n_flags = flags; 44052574Ssklower np->n_addr = (struct sockaddr *)(np + 1); 44152574Ssklower np->n_addrlen = dstaddr->sa_len; 44252574Ssklower np->n_name = np->n_addrlen + (char *)np->n_addr; 44312236Ssam np->n_next = neighbors; 44412236Ssam neighbors = np; 44552574Ssklower bcopy((char *)dstaddr, (char *)np->n_addr, np->n_addrlen); 44652574Ssklower bcopy(sdl->sdl_data, np->n_name, sdl->sdl_nlen); 44712236Ssam } 44852574Ssklower free(buf); 44912236Ssam return (1); 45012236Ssam } 45112807Ssam 45212807Ssam #ifdef DEBUG 45312807Ssam sendto(s, buf, cc, flags, to, tolen) 45412807Ssam int s; 45512807Ssam char *buf; 45612807Ssam int cc, flags; 45712807Ssam char *to; 45812807Ssam int tolen; 45912807Ssam { 46012807Ssam register struct whod *w = (struct whod *)buf; 46112807Ssam register struct whoent *we; 46212807Ssam struct sockaddr_in *sin = (struct sockaddr_in *)to; 46312807Ssam char *interval(); 46412807Ssam 46512807Ssam printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port)); 46612807Ssam printf("hostname %s %s\n", w->wd_hostname, 46713187Ssam interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 46812807Ssam printf("load %4.2f, %4.2f, %4.2f\n", 46913187Ssam ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 47013187Ssam ntohl(w->wd_loadav[2]) / 100.0); 47112807Ssam cc -= WHDRSIZE; 47212807Ssam for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) { 47313187Ssam time_t t = ntohl(we->we_utmp.out_time); 47412807Ssam printf("%-8.8s %s:%s %.12s", 47513187Ssam we->we_utmp.out_name, 47613187Ssam w->wd_hostname, we->we_utmp.out_line, 47713187Ssam ctime(&t)+4); 47813187Ssam we->we_idle = ntohl(we->we_idle) / 60; 47912807Ssam if (we->we_idle) { 48012807Ssam if (we->we_idle >= 100*60) 48112807Ssam we->we_idle = 100*60 - 1; 48212807Ssam if (we->we_idle >= 60) 48312807Ssam printf(" %2d", we->we_idle / 60); 48412807Ssam else 48512807Ssam printf(" "); 48612807Ssam printf(":%02d", we->we_idle % 60); 48712807Ssam } 48812807Ssam printf("\n"); 48912807Ssam } 49012807Ssam } 49112807Ssam 49212807Ssam char * 49312807Ssam interval(time, updown) 49412807Ssam int time; 49512807Ssam char *updown; 49612807Ssam { 49712807Ssam static char resbuf[32]; 49812807Ssam int days, hours, minutes; 49912807Ssam 50012807Ssam if (time < 0 || time > 3*30*24*60*60) { 50112807Ssam (void) sprintf(resbuf, " %s ??:??", updown); 50212807Ssam return (resbuf); 50312807Ssam } 50412807Ssam minutes = (time + 59) / 60; /* round to minutes */ 50512807Ssam hours = minutes / 60; minutes %= 60; 50612807Ssam days = hours / 24; hours %= 24; 50712807Ssam if (days) 50812807Ssam (void) sprintf(resbuf, "%s %2d+%02d:%02d", 50912807Ssam updown, days, hours, minutes); 51012807Ssam else 51112807Ssam (void) sprintf(resbuf, "%s %2d:%02d", 51212807Ssam updown, hours, minutes); 51312807Ssam return (resbuf); 51412807Ssam } 51512807Ssam #endif 516