aboutsummaryrefslogtreecommitdiff
path: root/dummyshell.c
diff options
context:
space:
mode:
Diffstat (limited to 'dummyshell.c')
-rw-r--r--dummyshell.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/dummyshell.c b/dummyshell.c
new file mode 100644
index 0000000..aa3c17a
--- /dev/null
+++ b/dummyshell.c
@@ -0,0 +1,622 @@
+/*
+ * build with: gcc -Wall -O2 -D_GNU_SOURCE=1 -D_HAS_CMD=1 -D_HAS_MSG=1 -D_HAS_HOSTENT=1 -D_HAS_SIGNAL=1 -D_HAS_UTMP=1 -D_HAS_SYSINFO -ffunction-sections -fdata-sections -ffast-math -fomit-frame-pointer dummyshell.c -o dummyshell
+ * strip -s dummyshell
+ */
+
+
+#ifdef _HAVE_CONFIG
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h> /* isprint(...) */
+#include <sys/ioctl.h> /* ioctl(...) */
+#include <stdint.h> /* UINT8_MAX */
+#include <string.h> /* memset(...) */
+#include <sys/types.h>
+#include <pwd.h> /* getpwuid(...) */
+#ifdef _HAS_CMD
+#include <sys/wait.h>
+#else
+#warning "COMMANDS(_HAS_CMD) disabled!"
+#endif
+#ifdef _HAS_MSG
+#include <errno.h>
+#include <string.h>
+#else
+#warning "MESSAGE(_HAS_MSG) disabled!"
+#endif
+#ifdef _HAS_HOSTENT
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#else
+#warning "HOSTENT(_HAS_HOSTENT) disabled!"
+#endif
+#ifdef _HAS_SIGNAL
+#include <signal.h> /* signal(...) */
+#else
+#warning "SIGNAL(_HAS_SIGNAL) disabled!"
+#endif
+#ifdef _HAS_UTMP
+#include <utmp.h> /* utmp structure */
+#else
+#warning "UTMP(_HAS_UTMP) disabled!"
+#endif
+#ifdef _HAS_SYSINFO
+#include "sys/types.h" /* sysinfo structture */
+#include "sys/sysinfo.h" /* sysinfo(...) */
+#else
+#warning "SYSINFO(_HAS_SYSINFO) disabled!"
+#endif
+
+/* for print_memusage() and print cpuusage() see: http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process */
+
+
+static const char keymsg[] = " ['q'-EXIT | 'm'-MESSAGE | 'c'-CMDS] ";
+static const char txtheader[] =
+ "**************\n"
+ "* dummyshell *\n"
+ "**************\n"
+ "!(C) by Toni Uhlig\n"
+ "@see: https://raw.githubusercontent.com/lnslbrty/foo-scripts/master/dummyshell.c\n";
+
+static volatile unsigned char doLoop = 1;
+
+
+static void printQuitLoop(void) {
+ printf("quit in 3 .. ");
+ fflush(stdout);
+ sleep(1);
+ printf("2 .. ");
+ fflush(stdout);
+ sleep(1);
+ printf("1 .. ");
+ fflush(stdout);
+ sleep(1);
+ printf("\n");
+ doLoop = 0;
+}
+
+#define I_CLEARBUF 0x1
+static char readInput(char* buf, size_t* siz, size_t szMax, char key, int flags) {
+ if (flags & I_CLEARBUF) {
+ memset(&buf[0], '\0', szMax);
+ *siz = 0;
+ } else switch (key) {
+ case '\n':
+ break;
+ case 127:
+ if (*siz > 0)
+ buf[--(*siz)] = '\0';
+ break;
+ case EOF: break;
+ default:
+ if (isprint(key) && *siz < szMax)
+ buf[(*siz)++] = key;
+ break;
+ }
+ return key;
+}
+
+#ifdef _HAS_CMD
+struct __attribute__((__packed__)) cmd {
+ char* name;
+ char* path;
+ char* defargs;
+};
+
+static struct cmd cmds[] = {
+ { "ether-wake", "/var/media/ftp/bin/suid-ether-wake", "-b -i lan" },
+ { "echo", "/bin/echo", NULL },
+ { NULL, NULL, NULL }
+};
+
+static void print_cmds(void)
+{
+ size_t idx = 0;
+ printf("\33[2K\r[COMMANDS]\n");
+ while ( cmds[idx++].path != NULL ) {
+ printf(" [%lu] %s\n", (unsigned long int)idx-1, ( cmds[idx-1].name != NULL ? cmds[idx-1].name : "unknown" ));
+ }
+}
+
+int safe_exec(const char* cmdWithArgs)
+{
+ pid_t child;
+ if ( (child = fork()) == 0 ) {
+ size_t szCur = 0, szMax = 10;
+ char** args = calloc(szMax, sizeof(char**));
+ const char* cmd = NULL;
+
+ const char* prv = cmdWithArgs;
+ const char* cur = NULL;
+ while ( (cur = strchr(prv, ' ')) ) {
+ if (cmd == NULL)
+ cmd = strndup(prv, cur-prv);
+
+ args[szCur++] = strndup(prv, cur-prv);
+ if (szCur >= szMax) {
+ szMax *= 2;
+ args = realloc(args, sizeof(char**)*szMax);
+ }
+
+ cur++;
+ prv = cur;
+ }
+ if (cmd == NULL) {
+ cmd = cmdWithArgs;
+ } else {
+ args[szCur++] = strndup(prv, cur-prv);
+ }
+ args[szCur] = NULL;
+ execv(cmd, args);
+ exit(-5);
+ } else if (child != -1) {
+ int retval = 0;
+ waitpid(child, &retval, 0);
+ return retval;
+ }
+ return -6;
+}
+
+static int exec_cmd(size_t i, char* args, size_t szArgs)
+{
+ size_t idx = (size_t)-1;
+ while ( cmds[++idx].path != NULL ) {
+ if (idx == i) {
+ size_t siz = strlen(cmds[idx].path)+szArgs+1;
+ char execbuf[siz+1];
+ memset(&execbuf[0], '\0', siz+1);
+ snprintf(&execbuf[0], siz+1, "%s %s", cmds[idx].path, args);
+ return safe_exec(&execbuf[0]);
+ }
+ }
+ return -7;
+}
+#endif
+
+#ifdef _HAS_MSG
+#define MSGFILE "/tmp/dummyshell.msg"
+#define STRLEN(str) (sizeof(str)/sizeof(str[0]))
+static int msgfd = -1;
+static int init_msg(void)
+{
+ msgfd = open(MSGFILE, O_RDWR | O_CREAT | O_APPEND | O_DSYNC | O_RSYNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (msgfd < 0) {
+ fprintf(stderr, "fopen(\"%s\") with write access: %s\n", MSGFILE, strerror(errno));
+ msgfd = open(MSGFILE, O_RDONLY | O_CREAT | O_APPEND | O_DSYNC | O_RSYNC);
+ if (msgfd < 0) {
+ fprintf(stderr, "fopen(\"%s\") readonly: %s\n", MSGFILE, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+struct __attribute__((__packed__)) msgHdr {
+ uint8_t szFrom;
+ uint8_t szMsg;
+ time_t timestamp;
+};
+
+struct __attribute__((__packed__)) msg {
+ char* from;
+ char* msg;
+};
+
+static int read_msg(struct msgHdr* hdr, struct msg* msg)
+{
+ if (msgfd < 0) return -1;
+ int ok = 1;
+ size_t rb = 0;
+ if ( (rb = read(msgfd, hdr, sizeof(struct msgHdr)*1)) == sizeof(struct msgHdr)*1 ) {
+ msg->from = calloc(hdr->szFrom+1, sizeof(char));
+ msg->msg = calloc(hdr->szMsg+1, sizeof(char));
+ if ( (rb = read(msgfd, &(msg->from[0]), sizeof(char)*hdr->szFrom)) != sizeof(char)*hdr->szFrom )
+ ok = 0;
+ if ( (rb = read(msgfd, &(msg->msg[0]), sizeof(char)*hdr->szMsg)) != sizeof(char)*hdr->szMsg )
+ ok = 0;
+ char newline = 0;
+ if ( (rb = read(msgfd, &newline, sizeof(char)*1)) != sizeof(char)*1 )
+ ok = 0;
+ if (!ok || newline != '\n') {
+ free(msg->from);
+ free(msg->msg);
+ msg->from = NULL;
+ msg->msg = NULL;
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static int print_msg(void) {
+ struct msgHdr hdr;
+ struct msg msg;
+ memset(&hdr, '\0', sizeof(struct msgHdr));
+ memset(&msg, '\0', sizeof(struct msg));
+ if (read_msg(&hdr, &msg) == 0) {
+ struct tm localtime;
+ struct passwd* pwd = NULL;
+ unsigned long int uid = strtoul(msg.from, NULL, 10);
+ if ( (pwd = getpwuid((uid_t)uid)) ) {
+ free(msg.from);
+ msg.from = strdup(pwd->pw_name);
+ }
+ if (localtime_r(&hdr.timestamp, &localtime) != NULL) {
+ printf("\33[2K\r[%02d-%02d-%04d %02d:%02d:%02d] Message from %s: %s\n", localtime.tm_mday, localtime.tm_mon+1, 1900+localtime.tm_year, localtime.tm_hour, localtime.tm_min, localtime.tm_sec, msg.from, msg.msg);
+ } else {
+ printf("\33[2K\r\aMessage from %s: %s\n", msg.from, msg.msg);
+ }
+ free(msg.from);
+ free(msg.msg);
+ return 0;
+ }
+ return -1;
+}
+
+static int write_msg(char* msg) {
+ char from[6];
+ memset(&from[0], '\0', STRLEN(from));
+ if (snprintf(&from[0], STRLEN(from), "%u", getuid()) > 0) {
+ struct msgHdr hdr;
+ hdr.szFrom = strnlen(from, STRLEN(from));
+ hdr.szMsg = strnlen(msg, UINT8_MAX);
+ hdr.timestamp = time(NULL);
+ char* buf = calloc(sizeof(hdr) + hdr.szFrom + hdr.szMsg + 2, sizeof(char));
+ if (buf) {
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(buf+sizeof(hdr), from, hdr.szFrom);
+ memcpy(buf+sizeof(hdr)+hdr.szFrom, msg, hdr.szMsg);
+ *(buf + sizeof(hdr) + hdr.szFrom + hdr.szMsg) = '\n';
+ int failed = 1;
+ if ( write(msgfd, buf, sizeof(char)*(sizeof(hdr)+hdr.szFrom+hdr.szMsg+1)) == sizeof(char)*(sizeof(hdr)+hdr.szFrom+hdr.szMsg+1) )
+ failed = 0;
+ free(buf);
+ return failed;
+ }
+ }
+ return -1;
+}
+#endif
+
+#ifdef _HAS_HOSTENT
+#define ARP_STRING_LEN 1024
+#define ARP_IP_LEN 32
+#define XSTR(s) STR(s)
+#define STR(s) #s
+static void print_nethost(void)
+{
+ FILE *arpCache = fopen("/proc/net/arp", "r");
+ if (arpCache != NULL) {
+ char arpline[ARP_STRING_LEN+1];
+ memset(&arpline[0], '\0', ARP_STRING_LEN+1);
+ if (fgets(arpline, ARP_STRING_LEN, arpCache)) {
+ char arpip[ARP_IP_LEN+1];
+ memset(&arpip[0], '\0', ARP_IP_LEN);
+ const char nonline[] = "\33[2K\rhost online...: ";
+ size_t i = 0;
+ while (1 == fscanf(arpCache, "%" XSTR(ARP_IP_LEN) "s %*s %*s %*s %*s %*s", &arpip[0])) {
+ struct in_addr ip;
+ struct hostent *hp = NULL;
+ if (inet_aton(&arpip[0], &ip)) {
+ hp = gethostbyaddr((const void *)&ip, sizeof ip, AF_INET);
+ }
+ char *herrmsg = NULL;
+ if (hp == NULL) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND: herrmsg = "HOST UNKNOWN"; break;
+ case NO_ADDRESS: herrmsg = "IP UNKNOWN"; break;
+ case NO_RECOVERY: herrmsg = "SERVER ERROR"; break;
+ case TRY_AGAIN: herrmsg = "TEMPORARY ERROR"; break;
+ }
+ }
+ printf("%s[%lu] %.*s aka %s\n", nonline, (long unsigned int)++i, ARP_IP_LEN, arpip, (hp != NULL ? hp->h_name : herrmsg));
+ memset(&arpip[0], '\0', ARP_IP_LEN);
+ }
+ }
+ fclose(arpCache);
+ }
+}
+#endif
+
+#ifdef _HAS_UTMP
+#ifndef _GNU_SOURCE
+size_t
+strnlen(const char *str, size_t maxlen)
+{
+ const char *cp;
+ for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--);
+ return (size_t)(cp - str);
+}
+#endif
+
+static void print_utmp(void)
+{
+ int utmpfd = open("/var/run/utmp", O_RDONLY);
+ if (utmpfd >= 0) {
+ struct utmp ut;
+ memset(&ut, '\0', sizeof(struct utmp));
+ const char uonline[] = "\33[2K\ruser online...: ";
+ size_t i = 0;
+ while ( read(utmpfd, &ut, sizeof(struct utmp)) == sizeof(struct utmp) && strnlen(ut.ut_user, UT_NAMESIZE) > 0 ) {
+ if (strnlen(ut.ut_host, UT_HOSTSIZE) > 0) {
+ printf("%s[%lu] %.*s from %.*s\n", uonline, (long unsigned int)++i, UT_NAMESIZE, ut.ut_user, UT_HOSTSIZE, ut.ut_host);
+ } else {
+ printf("%s[%lu] %.*s\n", uonline, (long unsigned int)++i, UT_NAMESIZE, ut.ut_user);
+ }
+ }
+ }
+}
+#endif
+
+#ifdef _HAS_SYSINFO
+static unsigned long long lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle;
+
+static void init_cpuusage(){
+ FILE* file = fopen("/proc/stat", "r");
+ if (file) {
+ fscanf(file, "cpu %llu %llu %llu %llu", &lastTotalUser, &lastTotalUserLow,
+ &lastTotalSys, &lastTotalIdle);
+ fclose(file);
+ }
+}
+
+static void print_cpuusage(){
+ double percent;
+ FILE* file;
+ unsigned long long totalUser, totalUserLow, totalSys, totalIdle, total;
+
+ file = fopen("/proc/stat", "r");
+ fscanf(file, "cpu %llu %llu %llu %llu", &totalUser, &totalUserLow,
+ &totalSys, &totalIdle);
+ fclose(file);
+
+ if (totalUser < lastTotalUser || totalUserLow < lastTotalUserLow ||
+ totalSys < lastTotalSys || totalIdle < lastTotalIdle){
+ //Overflow detection. Just skip this value.
+ percent = -1.0;
+ } else{
+ total = (totalUser - lastTotalUser) + (totalUserLow - lastTotalUserLow) +
+ (totalSys - lastTotalSys);
+ percent = total;
+ total += (totalIdle - lastTotalIdle);
+ percent /= total;
+ percent *= 100;
+ }
+
+ lastTotalUser = totalUser;
+ lastTotalUserLow = totalUserLow;
+ lastTotalSys = totalSys;
+ lastTotalIdle = totalIdle;
+
+ printf("CPU...........: %.02f%%\n", percent);
+}
+
+static void print_memusage(void)
+{
+ struct sysinfo meminfo;
+ memset(&meminfo, '\0', sizeof(struct sysinfo));
+ if (sysinfo(&meminfo) == 0) {
+ unsigned long long totalvmem = meminfo.totalram;
+ totalvmem += meminfo.totalswap;
+ totalvmem *= meminfo.mem_unit;
+ unsigned long long usedvmem = meminfo.totalram - meminfo.freeram;
+ usedvmem += meminfo.totalswap - meminfo.freeswap;
+ usedvmem *= meminfo.mem_unit;
+ printf("VMEM(used/max): %llu/%lld (Mb)\n", (usedvmem/(1024*1024)), (totalvmem/(1024*1024)));
+ }
+}
+#endif
+
+#ifdef _HAS_SIGNAL
+void SigIntHandler(int signum)
+{
+ if (signum == SIGINT) {
+ doLoop = 0;
+ }
+}
+#endif
+
+enum mainState { MS_DEFAULT, MS_MESSAGE, MS_COMMAND };
+
+int main(int argc, char** argv)
+{
+ enum mainState state = MS_DEFAULT;
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+#ifdef _HAS_SIGNAL
+ signal(SIGINT, SigIntHandler);
+#endif
+ size_t inputsiz = 0, absiz = UINT8_MAX;
+ struct winsize wsiz;
+ ioctl(0, TIOCGWINSZ, &wsiz);
+ if (wsiz.ws_col < absiz)
+ absiz = wsiz.ws_col - 3;
+ char inputbuf[absiz+1];
+ memset(&inputbuf[0], '\0', absiz+1);
+#ifdef _HAS_MSG
+ if (init_msg() != 0)
+ return 1;
+ if (argc > 1) {
+ const char optRmsg[] = "readmsg";
+ const char optWmsg[] = "writemsg";
+ const char optNofollow[] = "-n";
+ if (strncmp(argv[1], optRmsg, STRLEN(optRmsg)) == 0) {
+ if (argc == 2) while (doLoop) {
+ if (print_msg() != 0)
+ sleep(1);
+ } else if (argc == 3) {
+ if (strncmp(argv[2], optNofollow, STRLEN(optNofollow)) == 0) {
+ while (print_msg() == 0) {}
+ } else {
+ fprintf(stderr, "%s readmsg [-n]\n", argv[0]);
+ return 1;
+ }
+ }
+ return 0;
+ } else if (strncmp(argv[1], optWmsg, STRLEN(optWmsg)) == 0) {
+ if (argc == 3) {
+ return write_msg(argv[2]);
+ } else {
+ fprintf(stderr, "%s writemsg msg\n", argv[0]);
+ }
+ return 0;
+ } else {
+ fprintf(stderr, "usage: %s [readmsg [-n] | writemsg msg]\n", argv[0]);
+ return 1;
+ }
+ }
+#endif
+#ifdef _HAS_SYSINFO
+ init_cpuusage();
+#endif
+ int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
+ fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
+
+ static struct termios oldt, newt;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+ printf("%s\n", txtheader);
+ fd_set fds;
+ time_t start = time(NULL);
+ time_t cur;
+ unsigned char mins = 0, hrs = 0;
+ while (doLoop > 0) {
+ cur = time(NULL);
+ double diff = difftime(cur, start);
+#if defined(_HAS_UTMP) || defined(_HAS_SYSINFO)
+ if ((unsigned int)diff % 60 == 0) {
+ if (diff != 0 && ++mins == 60) {
+ mins = 0;
+ hrs++;
+ }
+ struct tm localtime;
+ if (localtime_r(&cur, &localtime) != NULL) {
+ printf("\33[2K\r--- %02d:%02d:%02d ---\n", localtime.tm_hour, localtime.tm_min, localtime.tm_sec);
+ }
+#ifdef _HAS_UTMP
+ print_utmp();
+#endif
+#ifdef _HAS_HOSTENT
+ print_nethost();
+#endif
+#ifdef _HAS_SYSINFO
+ print_memusage();
+ print_cpuusage();
+#endif
+ }
+#endif
+#ifdef _HAS_MSG
+ while (print_msg() == 0) {}
+#endif
+ switch (state) {
+ case MS_DEFAULT:
+ printf("\r--- %02d:%02d:%02d ---%s", hrs, mins, ((unsigned int)diff % 60), keymsg);
+ break;
+ case MS_MESSAGE:
+ case MS_COMMAND:
+#if defined(_HAS_MSG) || defined(_HAS_CMD)
+ printf("\33[2K\r> %s", inputbuf);
+#endif
+ break;
+ }
+ fflush(stdout);
+
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ int ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv);
+ if (doLoop == 1 && ret == 0) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ } else if (FD_ISSET(STDIN_FILENO, &fds)) {
+ char key = getchar();
+ switch (state) {
+ case MS_DEFAULT:
+ switch ( key ) {
+ case 'q':
+#ifdef _HAS_SIGNAL
+ signal(SIGINT, SIG_IGN);
+#endif
+ printQuitLoop();
+ break;
+ case 'm':
+#ifdef _HAS_MSG
+ state = MS_MESSAGE;
+#else
+ printf("<feature disabled>\n");
+#endif
+ break;
+ case 'c':
+#ifdef _HAS_CMD
+ print_cmds();
+ state = MS_COMMAND;
+#else
+ printf("<feature disabled>\n");
+#endif
+ break;
+ default: printf("unknown key: %c\n", key); break;
+ }
+ break;
+ case MS_COMMAND:
+ case MS_MESSAGE:
+ switch (readInput(&inputbuf[0], &inputsiz, absiz, key, 0)) {
+ case '\n':
+ if (state == MS_MESSAGE) {
+#ifdef _HAS_MSG
+ printf("\33[2K\rSending message(%lu): %s\n", (long unsigned int)inputsiz, inputbuf);
+ if (write_msg(inputbuf) != 0)
+ printf("Sending failed.\n");
+#endif
+ } else if (state == MS_COMMAND) {
+#ifdef _HAS_CMD
+ int inputFail = 0;
+ if (inputsiz < 3) {
+ inputFail++;
+ } else {
+ char* endptr = NULL;
+ unsigned long int tmpi = strtoul(inputbuf, &endptr, 10);
+ if (*endptr == ' ') {
+ endptr++;
+ printf("\33[2K\rExec CMD #%lu with args: %s\n", tmpi, endptr);
+ int retval;
+ switch ( (retval = exec_cmd(tmpi, endptr, strlen(endptr))) ) {
+ case -7: printf("unknown cmd #%lu\n", tmpi); break;
+ case -6: printf("fork error cmd #%lu\n", tmpi); break;
+ case -5: printf("execute cmd #%lu\n", tmpi); break;
+
+ case 0: break;
+ default: printf("Something went wrong, child returned: %d\n", retval); break;
+ }
+ } else inputFail++;
+ }
+ if (inputFail > 0)
+ printf("\33[2K\rFORMAT: [cmd#] [params]\n");
+#endif
+ }
+ state = MS_DEFAULT;
+ readInput(&inputbuf[0], &inputsiz, absiz, 0, I_CLEARBUF);
+ break;
+ }
+ fflush(stdout);
+ break;
+ }
+ }
+ }
+ while (getchar() != EOF) {}
+
+ tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
+ return 0;
+}