/* System monitor command-line client: obtains system information from * remote machines */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sysmon.h" /*************************************************************************/ char *server = "localhost"; /* Server to use */ int port = DEFAULT_PORT; /* Port to use */ char *pass = NULL; /* Password for server */ int sock = -1; /* Socket descriptor */ int query; /* Query to send */ /*************************************************************************/ /*************************************************************************/ /* Convert a signal name or number to an integer. */ int sigtoi(const char *sig) { if (isdigit(*sig)) return atoi(sig); else if (strcasecmp(sig, "HUP") == 0) return SIGHUP; else if (strcasecmp(sig, "INT") == 0) return SIGINT; else if (strcasecmp(sig, "QUIT") == 0) return SIGQUIT; else if (strcasecmp(sig, "ILL") == 0) return SIGILL; else if (strcasecmp(sig, "TRAP") == 0) return SIGTRAP; else if (strcasecmp(sig, "ABRT") == 0) return SIGABRT; else if (strcasecmp(sig, "IOT") == 0) return SIGIOT; else if (strcasecmp(sig, "BUS") == 0) return SIGBUS; else if (strcasecmp(sig, "FPE") == 0) return SIGFPE; else if (strcasecmp(sig, "KILL") == 0) return SIGKILL; else if (strcasecmp(sig, "USR1") == 0) return SIGUSR1; else if (strcasecmp(sig, "SEGV") == 0) return SIGSEGV; else if (strcasecmp(sig, "USR2") == 0) return SIGUSR2; else if (strcasecmp(sig, "PIPE") == 0) return SIGPIPE; else if (strcasecmp(sig, "ALRM") == 0) return SIGALRM; else if (strcasecmp(sig, "TERM") == 0) return SIGTERM; else if (strcasecmp(sig, "STKFLT") == 0) return SIGSTKFLT; else if (strcasecmp(sig, "CHLD") == 0) return SIGCHLD; else if (strcasecmp(sig, "CONT") == 0) return SIGCONT; else if (strcasecmp(sig, "STOP") == 0) return SIGSTOP; else if (strcasecmp(sig, "TSTP") == 0) return SIGTSTP; else if (strcasecmp(sig, "TTIN") == 0) return SIGTTIN; else if (strcasecmp(sig, "TTOU") == 0) return SIGTTOU; else if (strcasecmp(sig, "URG") == 0) return SIGURG; else if (strcasecmp(sig, "XCPU") == 0) return SIGXCPU; else if (strcasecmp(sig, "XFSZ") == 0) return SIGXFSZ; else if (strcasecmp(sig, "VTALRM") == 0) return SIGVTALRM; else if (strcasecmp(sig, "PROF") == 0) return SIGPROF; else if (strcasecmp(sig, "WINCH") == 0) return SIGWINCH; else if (strcasecmp(sig, "IO") == 0) return SIGIO; else if (strcasecmp(sig, "POLL") == 0) return SIGPOLL; else if (strcasecmp(sig, "PWR") == 0) return SIGPWR; else return 0; } /*************************************************************************/ /* Convert query names to and from query numbers. */ int name_to_query(const char *name) { if (strcasecmp(name, "password") == 0) return Q_PASSWORD; else if (strcasecmp(name, "close") == 0) return Q_CLOSE; else if (strcasecmp(name, "uptime") == 0) return Q_UPTIME; else if (strcasecmp(name, "diskfree") == 0) return Q_DISKFREE; else if (strcasecmp(name, "quota") == 0) return Q_QUOTA; else if (strcasecmp(name, "memory") == 0) return Q_MEMORY; else if (strcasecmp(name, "procstat") == 0) return Q_PROCSTAT; else if (strcasecmp(name, "procquery") == 0) return Q_PROCQUERY; else if (strcasecmp(name, "proclist") == 0) return Q_PROCLIST; else if (strcasecmp(name, "kill") == 0) return Q_KILL; else if (strcasecmp(name, "exec") == 0) return Q_EXEC; else if (strcasecmp(name, "reboot") == 0) return Q_REBOOT; else if (strcasecmp(name, "restart") == 0) return Q_RESTART; else return -1; } const char *query_to_name(int query) { switch (query) { case Q_PASSWORD: return "PASSWORD"; case Q_CLOSE: return "CLOSE"; case Q_UPTIME: return "UPTIME"; case Q_DISKFREE: return "DISKFREE"; case Q_QUOTA: return "QUOTA"; case Q_MEMORY: return "MEMORY"; case Q_PROCSTAT: return "PROCSTAT"; case Q_PROCQUERY: return "PROCQUERY"; case Q_PROCLIST: return "PROCLIST"; case Q_KILL: return "KILL"; case Q_EXEC: return "EXEC"; default: return "(unknown)"; } } /*************************************************************************/ /* Read from a socket, always getting the number of bytes requested unless * we hit an error (like connection closed). */ int my_read(int fd, char *buf, size_t size) { int n = 0, tot = 0; while (tot < size) { n = read(fd, buf+tot, size-tot); if (n == 0 || (n < 0 && errno != EAGAIN && errno != EINTR)) break; tot += n; if (tot < size) usleep(1000); } if (tot > 0) return tot; else return n; } /*************************************************************************/ /* Send a query. See sysmond.c/reply() for details on the format string. */ void send_query(int sock, unsigned char query, const char *format, ...) { const char *s; int len = 0; va_list args; char buf[4]; /* for writing integers */ va_start(args, format); s = format; while (*s) { if (*s++ == '%') { switch (*s++) { case 'd': (void) va_arg(args, int); len += 4; break; case 'c': (void) va_arg(args, int); len++; break; case 's': len += strlen(va_arg(args, char *)) + 1; break; } } else { len++; } } len++; /* Query code */ buf[0] = QUERY_MAGIC>>24; buf[1] = QUERY_MAGIC>>16 & 0xFF; buf[2] = QUERY_MAGIC>> 8 & 0xFF; buf[3] = QUERY_MAGIC & 0xFF; write(sock, buf, 4); buf[0] = len>>24; buf[1] = len>>16; buf[2] = len>> 8; buf[3] = len; write(sock, buf, 4); buf[0] = query; write(sock, buf, 1); va_start(args, format); while (*format) { if (*format == '%') { format++; switch (*format++) { case 'd': { unsigned int n = va_arg(args, unsigned int); buf[0] = n>>24; buf[1] = n>>16; buf[2] = n>> 8; buf[3] = n; write(sock, buf, 4); break; } /* case 'd' */ case 'c': { unsigned int n = va_arg(args, unsigned int); *buf = n; write(sock, buf, 1); break; } /* case 'c' */ case 's': { s = va_arg(args, const char *); write(sock, s, strlen(s)+1); break; } /* case 's' */ } /* switch (*format++) */ } else { write(sock, format++, 1); } } } /*************************************************************************/ /* Send a query given the raw query buffer and length. */ void send_raw_query(int sock, unsigned char query, const char *buffer, int len) { char buf[4]; /* for writing integers */ buf[0] = QUERY_MAGIC>>24; buf[1] = QUERY_MAGIC>>16 & 0xFF; buf[2] = QUERY_MAGIC>> 8 & 0xFF; buf[3] = QUERY_MAGIC & 0xFF; write(sock, buf, 4); buf[0] = (len+1)>>24; buf[1] = (len+1)>>16; buf[2] = (len+1)>> 8; buf[3] = (len+1); write(sock, buf, 4); buf[0] = query; write(sock, buf, 1); write(sock, buffer, len); } /*************************************************************************/ /* Read a response on the socket. */ char *read_response(int sock, unsigned char *query_ret) { static unsigned char *buf = NULL; int magic, len; char intbuf[4]; if (buf) free(buf); if (my_read(sock, intbuf, 4) != 4) { fprintf(stderr, "Unable to read magic number from server\n"); exit(1); } magic = ntohl(*(int *)intbuf); if (magic != RESPONSE_MAGIC) { fprintf(stderr, "Did not receive RESPONSE_MAGIC from server\n"); exit(1); } if (my_read(sock, intbuf, 4) != 4) { fprintf(stderr, "Unable to read response length from server\n"); exit(1); } len = ntohl(*(int *)intbuf); buf = malloc(len); if (!buf) { fprintf(stderr, "Could not allocate buffer for response\n"); exit(1); } if (my_read(sock, buf, len) != len) { fprintf(stderr, "Unable to read response from server\n"); exit(1); } if (query_ret) *query_ret = buf[0]; return buf+1; } /*************************************************************************/ /* Quick routines to get an int or string out of the result buffer. Pass * a pointer to the buffer pointer, which will be updated. */ int getint(unsigned char **result) { register unsigned char *s = *result; register int val; val = *s++; val = val<<8 | *s++; val = val<<8 | *s++; val = val<<8 | *s++; *result = (char *)s; return val; } char *getstr(unsigned char **result) { register unsigned char *s = *result; char *ret = (char *) s; while (*s++) ; *result = s; return ret; } /*************************************************************************/ /*************************************************************************/ void usage(const char *progname) { fprintf(stderr, "Usage: %s [pass@]server[:port] query [params...]\n", progname); exit(1); } /*************************************************************************/ void init(int *acptr, char ***avptr) { struct hostent *he; struct protoent *pe; struct sockaddr_in sin; char *s; int ac = *acptr; char **av = *avptr; if (ac < 3) usage(av[0]); s = strchr(av[1], '@'); if (s) { *s++ = 0; pass = av[1]; pass = strdup(pass); memset(av[1], 0, s-av[1]); av[1] = s; } s = strchr(av[1], ':'); if (s) { *s++ = 0; port = atoi(s); } server = av[1]; query = name_to_query(av[2]); *acptr = ac-3; *avptr = av+3; pe = getprotobyname("tcp"); if (!pe) { perror("getprotobyname(tcp)"); exit(1); } he = gethostbyname(server); if (!he) { fprintf(stderr, "gethostbyname(%s): %s\n", server, strerror(errno)); exit(1); } sock = socket(AF_INET, SOCK_STREAM, pe->p_proto); if (sock < 0) { perror("socket()"); exit(1); } sin.sin_family = AF_INET; memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr)); sin.sin_port = htons(port); if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { fprintf(stderr, "connect(%s:%d): %s\n", server, port, strerror(errno)); exit(1); } } /*************************************************************************/ int main(int ac, char **av) { const char *s; unsigned char *result; unsigned char q; init(&ac, &av); if (!pass) pass = getpass("Password: "); send_query(sock, Q_PASSWORD, "%s", pass); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != Q_PASSWORD) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else if (!getint(&result)) { fprintf(stderr, "Password incorrect.\n"); return 1; } switch (query) { case Q_PASSWORD: if (ac < 1) { fprintf(stderr, "Not enough arguments for PASSWORD.\n"); break; } send_query(sock, query, "%s", av[0]); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else if (!getint(&result)) { printf("Password incorrect.\n"); } else { printf("Password correct.\n"); } break; case Q_CLOSE: fprintf(stderr, "Cannot query CLOSE.\n"); break; case Q_UPTIME: send_query(sock, query, ""); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { time_t t = time(NULL); struct tm *tm = localtime(&t); char ampm, buf1[32], buf2[16]; int uptime, load1, load5, load15, nusers, nprocs; uptime = getint(&result); load1 = getint(&result); load5 = getint(&result); load15 = getint(&result); nusers = getint(&result); nprocs = getint(&result); if (tm->tm_hour > 12) { tm->tm_hour -= 12; ampm = 'p'; } else { ampm = 'a'; } if (tm->tm_hour == 0) tm->tm_hour = 12; if (uptime >= 86400) { sprintf(buf1, "%d day%s, ", uptime/86400, uptime >= 172800 ? "s" : ""); } else { *buf1 = 0; } if ((uptime/3600) % 24 == 0) { sprintf(buf2, "%2d min", (uptime/60) % 60); } else { sprintf(buf2, "%2d:%02d", (uptime/3600) % 24, (uptime/60) % 60); } printf(" %2d:%02d%cm up %s%s, %2d users," " load average: %d.%02d, %d.%02d, %d.%02d\n", tm->tm_hour, tm->tm_min, ampm, buf1, buf2, nusers, load1/100, load1%100, load5/100, load5%100, load15/100, load15%100); } break; case Q_DISKFREE: if (ac < 1) { fprintf(stderr, "Not enough arguments for DISKFREE.\n"); break; } send_query(sock, query, "%s", av[0]); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { int bsize, blocks, bused, bresv, inodes, iused, iresv; getstr(&result); bsize = getint(&result); blocks = getint(&result); bused = getint(&result); bresv = getint(&result); inodes = getint(&result); iused = getint(&result); iresv = getint(&result); if (bsize > 1024) { blocks *= bsize/1024; bused *= bsize/1024; bresv *= bsize/1024; } else { blocks /= 1024/bsize; bused /= 1024/bsize; bresv /= 1024/bsize; } if (blocks-bused < bresv) blocks = bused; else blocks -= bresv; if (inodes-iused < iresv) inodes = iused; else inodes -= iresv; printf("%s: %d/%d 1024-blocks, %d/%d inodes\n", av[0], bused, blocks, iused, inodes); } break; case Q_QUOTA: if (ac < 2) { fprintf(stderr, "Not enough arguments for QUOTA.\n"); break; } send_query(sock, query, "%s%d", av[0], atoi(av[1])); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } break; case Q_MEMORY: send_query(sock, query, ""); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { int memtotal, memfree, shared, buffers, cached, swaptotal, swapfree; memtotal = getint(&result); memfree = getint(&result); shared = getint(&result); buffers = getint(&result); cached = getint(&result); swaptotal = getint(&result); swapfree = getint(&result); printf(" %10s %10s %10s %10s %10s %10s\n", "total", "used", "free", "shared", "buffers", "cached"); printf("Mem: %10d %10d %10d %10d %10d %10d\n", memtotal, memtotal-memfree, memfree, shared, buffers, cached); memfree += buffers + cached; printf("-/+ buffers: %10d %10d\n", memtotal-memfree, memfree); printf("Swap: %10d %10d %10d\n", swaptotal, swaptotal-swapfree, swapfree); } break; case Q_PROCSTAT: if (ac < 1) { fprintf(stderr, "Not enough arguments for PROCSTAT.\n"); break; } send_query(sock, query, "%d", atoi(av[0])); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { fputs(result, stdout); } break; case Q_PROCQUERY: if (ac < 1) { fprintf(stderr, "Not enough arguments for PROCQUERY.\n"); break; } send_query(sock, query, "%s", av[0]); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { int count; getstr(&result); count = getint(&result); printf("PIDs for %s:", av[0]); while (count--) printf(" %d", getint(&result)); putchar('\n'); } break; case Q_PROCLIST: send_query(sock, query, ""); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } else { int count = getint(&result); printf("PIDs on system:"); while (count--) printf(" %d", getint(&result)); putchar('\n'); } break; case Q_KILL: if (ac < 1) { fprintf(stderr, "Not enough arguments for KILL.\n"); break; } send_query(sock, query, "%d%d", atoi(av[1]), sigtoi(av[0]+1)); result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } break; case Q_EXEC: if (ac < 1) { fprintf(stderr, "Not enough arguments for EXEC.\n"); break; } { char buf[4096], *ptr, *countptr; int count = 0; strncpy(buf, av[0], sizeof(buf)-4); buf[sizeof(buf)-5] = 0; ptr = buf + strlen(buf) + 1; ac--; av++; countptr = ptr; ptr += 4; while (count < ac && ptr-buf < sizeof(buf)) { strncpy(ptr, av[count], sizeof(buf) - (ptr-buf)); buf[sizeof(buf)-1] = 0; ptr += strlen(ptr) + 1; count++; } *countptr++ = count>>24; *countptr++ = count>>16; *countptr++ = count>> 8; *countptr++ = count; send_raw_query(sock, query, buf, ptr-buf); } result = read_response(sock, &q); if (q == Q_ERROR) { s = query_to_name(*result++); fprintf(stderr, "Server returned error: %s: %s\n", s, getstr(&result)); return 1; } else if (q != query) { fprintf(stderr, "Unexpected response type %s\n", query_to_name(q)); return 1; } break; case Q_REBOOT: if (av[0] && strcmp(av[0], "quick") == 0) send_query(sock, query, "%d", 1); else send_query(sock, query, "%d", 0); break; case Q_RESTART: send_query(sock, query, ""); break; default: fprintf(stderr, "Unknown query type %d.\n", query); break; } return 0; } /*************************************************************************/