/* A standalone shell which should not require other programs and PATHs * etc to run. * This executable should be statically linked so as to be minimally reliant * on shared libraries. * gcc -static-libgcc -static -o dsh dsh.c * * cl dsh.c Advapi32.lib Psapi.lib * * David Horton, 2008 */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include "psapi.h" #endif #include #define MAX_LINE 10000 #define MAX_CMDS 256 #define MAX_PATH_SZ 256 #define NO_SUCH_COMMAND 27345 static char rcs[] = "$Id$"; static char PS1[100] = "$ "; static int v_quit = 0; static int s_ver(int argc, char *argv[]); static int s_quit(int argc, char *argv[]); static int s_cat(int argc, char *argv[]); static int s_help(int argc, char *argv[]); static int s_prompt(int argc, char *argv[]); static int s_pwd(int argc, char *argv[]); static int s_cd(int argc, char *argv[]); static int s_ls(int argc, char *argv[]); static int s_id(int argc, char *argv[]); static int s_ps(int argc, char *argv[]); #ifndef _WIN32 static int s_su(int argc, char *argv[]); #endif #ifdef _WIN32 #define snprintf(b,s,c,v1,v2) sprintf(b,c,v1,v2) #define FAIL_CODE GetLastError() #else #define FAIL_CODE errno #endif static int split_command(char *command, int *argc, char *argv[]); static struct { char *s_command; int (*s_function)(int argc, char *argv[]); char *usage; } dispatch_table[] = { {"cat", s_cat, "Display a file. Usage: cat file"}, {"cd", s_cd, "Change current working directory"}, {"help", s_help, "Display help on all available commands"}, {"id", s_id, "Display real and effective UIDs and GIDs"}, {"ls", s_ls, "List files [-l for details]. Usage: ls [-l] [directory]" #ifdef _WIN32 "\n\t[d]irectory [?]encrypted [C]ompressed [S]ystem [H]idden [r]ead [w]write" #endif }, {"man", s_help, "(same as 'help')"}, {"prompt", s_prompt, "Set the command line prompt. Usage: prompt NEWPROMPT"}, {"ps", s_ps,"Process status"}, {"pwd", s_pwd, "Print current working directory"}, {"quit", s_quit, "Log out from this shell"}, #ifndef WIN32 {"su", s_su, "Set user. Usage: su { uid N | euid N | gid N | egid N }"}, #endif {"ver", s_ver, "Show shell version"}, }; int main(int argc, char *argv[]) { int a,c; char commandline[MAX_LINE]; char lasterror[MAX_LINE]; int exitval; char *sep; int s_argc; char *s_argv[MAX_CMDS]; #ifndef WIN32 if (0 == geteuid()) strcpy(PS1, "# "); #endif /* Parse argv for shell itself */ /* Read commands from standard input (could have been /dev/tty ?) * Split into commands and args * Lookup command table * Dispatch command * If status not success print warning */ do { printf("\r%s", PS1); if (NULL == fgets(commandline, sizeof(commandline), stdin)) break; (void) split_command(commandline, &s_argc, s_argv); if (s_argc > 0) { exitval = NO_SUCH_COMMAND; for (c = 0; c < sizeof(dispatch_table)/sizeof(dispatch_table[0]);c++) { if (0 == strcmp(s_argv[0],dispatch_table[c].s_command)) { exitval = (*dispatch_table[c].s_function)(s_argc, s_argv); break; } } switch (exitval) { case NO_SUCH_COMMAND: printf("No such command '%s'\n", s_argv[0]); break; case 0: break; default: for (a = 0, sep = ""; a < s_argc ; a++) { printf("%s%s", sep, s_argv[a]); sep = " "; } #ifndef _WIN32 printf(" : %d (errno = %d: %s)\n", exitval, errno, strerror(errno)); #else if (FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, "", exitval, 0, lasterror, sizeof(lasterror), NULL)) printf(" : %s", lasterror); else printf(" : %d (errno = %d: %s)\n", exitval, errno, strerror(errno)); #endif break; } } } while (v_quit == 0); exit(0); } static int split_command(char *command, int *argc, char *argv[]) { int a, c; int state = 0; /* 0 - waiting for first, or subsequent arg * 1 - waiting for end of arg * 2 - exiting */ for(a = 0, c = 0; (c < MAX_LINE) && (a < MAX_CMDS) && (state != 2); c++) { switch(state){ case 0: switch(command[c]) { case '\r': case '\n': case '\0': command[c] = '\0'; state = 2; break; case ' ': case '\t': break; /* skip blank space between args */ default: argv[a++] = &command[c]; state = 1; break; } break; case 1: switch(command[c]) { case '\r': case '\n': case '\0': command[c] = '\0'; state = 2; break; case ' ': case '\t': command[c] = '\0'; state = 0; break; default: break; /* accumulate command */ } break; }; } argv[a] = NULL; *argc = a; return 0; } /* =========================================================== */ /* ==== cat */ static int s_cat(int argc, char *argv[]) { char dest[1000]; char *sep = ""; int a; FILE *fd; memset(dest,0,sizeof(dest)); for(a = 1; a < argc; a++) { strcat(dest,sep); strcat(dest,argv[a]); sep = " "; } if (NULL == (fd = fopen(dest, "r"))){ return (FAIL_CODE); } while(fgets(dest,sizeof(dest), fd)) printf("%s", dest); fclose(fd); return (0); } /* ==== cd */ static int s_cd(int argc, char *argv[]) { char dest[1000]; char *sep = ""; int a; if(argc == 2) { return (chdir(argv[1])); } else { memset(dest,0,sizeof(dest)); for(a = 1; a < argc; a++) { strcat(dest,sep); strcat(dest,argv[a]); sep = " "; } return (chdir(dest)); } } /* ==== help */ static int s_help(int argc, char *argv[]) { int c; for (c = 0; c < sizeof(dispatch_table)/sizeof(dispatch_table[0]);c++) { if ((argc == 1) || (0 == strcmp(dispatch_table[c].s_command, argv[1]))) printf("%s\t%s\n", dispatch_table[c].s_command, dispatch_table[c].usage); } return 0; } /* ==== id */ #ifndef WIN32 static int s_id(int argc, char *argv[]) { int ret; uid_t uid, euid; gid_t gid, egid; uid = getuid(); euid = geteuid(); gid = getgid(); egid = getegid(); printf("UID: %d(real) %d(effective) GID: %d(real) %d(effective)\n", uid, euid, gid, egid); return 0; } #else static int s_id(int argc, char *argv[]) { TCHAR username[256]; DWORD lusername = sizeof(username); if(GetUserName(username,&lusername)) { printf("User name: %s\n",username); return (0); } else { return(FAIL_CODE); } } #endif /* ==== ls */ #ifndef _WIN32 static int s_ls_dir(char *dir, char *filter, int long_listing) { struct stat dot; int ret; int rv = 0; char full_path[MAX_LINE]; DIR *cwd = NULL; struct dirent *dir_p; char mod_time[100]; struct tm *mod_time_st; cwd = opendir(dir); if (cwd) { while ((dir_p = readdir(cwd)) != NULL) { if (long_listing) { /* Apply filename filter. Should push glob out to shell layer */ if (0 == strcmp(".", dir)) { snprintf(full_path, sizeof(full_path), "%s", dir_p->d_name, ""); } else { if (dir[strlen(dir)-1] == '/') { snprintf(full_path, sizeof(full_path), "%s%s", dir, dir_p->d_name); } else { snprintf(full_path, sizeof(full_path), "%s/%s", dir, dir_p->d_name); } } memset(&dot, 0, sizeof(dot)); if (0 == (ret = stat(full_path, &dot))) { /* Long listing */ #ifdef LONG_INCLUDES_INODE printf("%10d ", dot.st_ino); #endif if (S_ISDIR(dot.st_mode)) { printf("d"); } else { printf("-"); } if (S_IRUSR & dot.st_mode) { printf("r"); } else { printf("-"); } if (S_IWUSR & dot.st_mode) { printf("w"); } else { printf("-"); } if (S_ISUID & dot.st_mode) { printf("s"); } else if (S_IXUSR & dot.st_mode) { printf("x"); } else { printf("-"); } if (S_IRGRP & dot.st_mode) { printf("r"); } else { printf("-"); } if (S_IWGRP & dot.st_mode) { printf("w"); } else { printf("-"); } if (S_ISGID & dot.st_mode) { printf("s"); } else if (S_IXGRP & dot.st_mode) { printf("x"); } else { printf("-"); } if (S_IROTH & dot.st_mode) { printf("r"); } else { printf("-"); } if (S_IWOTH & dot.st_mode) { printf("w"); } else { printf("-"); } if (S_IXOTH & dot.st_mode) { printf("x"); } else { printf("-"); } printf(" %3d", dot.st_nlink); printf(" %7d %7d", dot.st_uid, dot.st_gid); printf(" %12d", dot.st_size); mod_time_st = localtime(&dot.st_mtime); strcpy(mod_time, "(unavailable)"); if(mod_time_st) { strftime(mod_time, sizeof(mod_time), "%Ex %EX", mod_time_st); } printf(" %s", mod_time); printf(" %s\n", full_path); } else { printf("%s : Error %d\n", full_path, ret); rv = ret; } } else { printf("%s\n", dir_p->d_name); } } (void)closedir(cwd); } else { rv = 1; } return rv; } #else static int s_ls_dir(char *dir, char *filter, int long_listing) { WIN32_FIND_DATA FindFileData; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD dwError; char DirSpec[MAX_LINE]; char full_path[MAX_LINE]; SYSTEMTIME st; FILETIME ft; char szLocalDate[255], szLocalTime[255]; int pad; sprintf(DirSpec, "%s\\*", dir); if( INVALID_HANDLE_VALUE == (hFind = FindFirstFile(DirSpec, &FindFileData))) { dwError = GetLastError(); goto end; } do { /* Apply filename filter. Should push glob out to shell layer */ if (0 == strcmp(".", dir)) { sprintf(full_path, "%s", FindFileData.cFileName); } else { if ((dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')) { sprintf(full_path, "%s%s", dir, FindFileData.cFileName); } else { sprintf(full_path, "%s/%s", dir, FindFileData.cFileName); } } if(long_listing) { if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) printf("d"); else printf("-"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) printf("?"); else printf("-"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) printf("C"); else printf("-"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) printf("S"); else printf("-"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) printf("H"); else printf("-"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) printf("-"); else printf("r"); if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) printf("- "); else printf("w "); if(FindFileData.nFileSizeHigh > 0) printf("%dG", FindFileData.nFileSizeHigh << 2); else printf("%10d ", FindFileData.nFileSizeLow); FileTimeToLocalFileTime(&FindFileData.ftLastWriteTime, &ft); FileTimeToSystemTime(&ft, &st); GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, szLocalDate, sizeof(szLocalDate) ); GetTimeFormat( LOCALE_USER_DEFAULT, 0, &st, NULL, szLocalTime, sizeof(szLocalTime) ); for(pad = strlen(szLocalDate); pad < 10; pad++) printf(" "); printf("%s ", szLocalDate); for(pad = strlen(szLocalTime); pad < 11; pad++) printf(" "); printf("%s ", szLocalTime); printf("%s\n",full_path); } else { printf("%s\n",full_path); } } while ( 0!= FindNextFile(hFind, &FindFileData)); dwError = GetLastError(); FindClose(hFind); end: switch(dwError) { case ERROR_NO_MORE_FILES: return 0; default: printf("0x%10.10x : %s\n", dwError, DirSpec); return dwError; } } #endif static int s_ls(int argc, char *argv[]) { struct stat dot; int ret; int rv = 0; int a; int long_listing = 0; int dirs_used = 0; for (a = 1; a < argc; a++) { if (0 == strcmp("-l", argv[a])) { long_listing = 1; continue; } if (0 != (ret = s_ls_dir(argv[a], "", long_listing))) rv = ret; dirs_used ++; } if (dirs_used == 0) return s_ls_dir(".", "", long_listing); return rv; } /* ==== prompt */ static int s_prompt(int argc, char *argv[]) { if(argc > 1) { snprintf(PS1,sizeof(PS1),"%s ", argv[1], ""); return (0); } else { return (1); } } /* ==== ps */ static int is_numeric(char *s) { int rv = 0; /* not numeric */ char *c; if (s == NULL) return 0; for(c = s; *c; c++) { if ( (*c >= '0') && (*c <= '9')) { rv = 1; } else { rv = 0; break; } } return rv; } #ifndef _WIN32 static int s_ps(int argc, char *argv[]) { struct stat dot; int ret; int rv = 0; int a, f, s; int flags = 0; char full_path[MAX_PATH_SZ]; DIR *cwd = NULL; struct dirent *dir_p; char cmdline[MAX_PATH_SZ]; char sbuf[MAX_PATH_SZ]; char ppid[MAX_PATH_SZ]; char *p; FILE *fd; size_t num_read; char *stat_files[] = {"stat", "status" }; for (a = 1; a < argc; a++) { for(f = 0; argv[a][f] != '\0'; f++) { switch(argv[a][f]) { case '-': continue; } } } cwd = opendir("/proc"); if (cwd) { while ((dir_p = readdir(cwd)) != NULL) { /* PID is dir_p->d_name */ if (! is_numeric(dir_p->d_name)) continue; strcpy(ppid, "?"); strcpy(cmdline, "(unavailable)"); for(s = 0; s < sizeof(stat_files)/sizeof(stat_files[0]); s++) { snprintf(full_path, sizeof(full_path), "/proc/%s/%s", dir_p->d_name, stat_files[s]); strcpy(sbuf, "(unavailable)"); if ( NULL != (fd = fopen(full_path, "r"))) { while (NULL != fgets(sbuf, sizeof(sbuf), fd)) { if(0 == strncasecmp("ppid", sbuf, 4)) { for(p = &sbuf[4]; *p != '\0'; p++) { if ( (*p >= '0') && (*p <= '9') ) { strcpy(ppid, p); if (ppid[strlen(ppid) - 1] == '\n') ppid[strlen(ppid) - 1] = '\0'; break; } } } if(0 == strncasecmp("name", sbuf, 4)) { for(p = &sbuf[4]; *p != '\0'; p++) { if ( *p == '\t' ) { strcpy(cmdline, p+1); break; } } } if(0 == strncasecmp("argv0", sbuf, 5)) { for(p = &sbuf[5]; *p != '\0'; p++) { if ( *p == '\t' ) { strcpy(cmdline, p+1); break; } } } } fclose(fd); } } snprintf(full_path, sizeof(full_path), "/proc/%s/cmdline", dir_p->d_name); if ( NULL != (fd = fopen(full_path, "r"))) { if (NULL == fgets(cmdline, sizeof(cmdline), fd)) { ; } fclose(fd); } if (cmdline[strlen(cmdline) - 1] == '\n') cmdline[strlen(cmdline) - 1] = '\0'; printf("%6s %6s %s\n", dir_p->d_name, ppid, cmdline); } (void)closedir(cwd); } else { rv = 1; } return rv; } #else static void s_ps_by_id(DWORD processID) { TCHAR szProcessName[MAX_PATH] = TEXT(""); PROCESS_MEMORY_COUNTERS pmc; FILETIME created = {0,0}; FILETIME exittime; FILETIME kerneltime = {0,0}; FILETIME usertime = {0,0}; SYSTEMTIME st; FILETIME ft; char szLocalTime[255]; DWORD Secs; char unit = ' '; // Get a handle to the process. HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); if (NULL != hProcess ) { // Get the process name. HMODULE hMod; DWORD cbNeeded; if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) { GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) ); } if (! GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ) { pmc.WorkingSetSize = 0; } else { if(pmc.WorkingSetSize > 10000) { pmc.WorkingSetSize /= 1000; unit = 'K'; } if(pmc.WorkingSetSize > 10000) { pmc.WorkingSetSize /= 1000; unit = 'M'; } } if (GetProcessTimes(hProcess, &created, &exittime, &kerneltime, &usertime)) { FileTimeToLocalFileTime(&created, &ft); FileTimeToSystemTime(&ft, &st); GetTimeFormat( LOCALE_USER_DEFAULT, 0, &st, NULL, szLocalTime, sizeof(szLocalTime) ); Secs=(usertime.dwHighDateTime << 9) | (usertime.dwLowDateTime >> 23) +(kerneltime.dwHighDateTime << 9) | (kerneltime.dwLowDateTime >> 23) ; } /* Print the process name and identifier. */ _tprintf( TEXT("%6u %5u%c%+12.12s %3d:%2.2d %s\n"), processID, pmc.WorkingSetSize, unit, szLocalTime, Secs / 60, Secs % 60, szProcessName); CloseHandle( hProcess ); } } static int s_ps(int argc, char *argv[]) { /* Get list of process identifiers */ DWORD aProcesses[1024]; /* can we get > 1024? */ DWORD cbNeeded; unsigned int i; if( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded)) return GetLastError(); /* Print process details */ printf(" PID MemUse Start Tot CPU Image\n"); for (i = 0; i < cbNeeded / sizeof(DWORD); i++) s_ps_by_id(aProcesses[i]); return (0); } #endif /* WIN32 */ /* ==== pwd */ #ifndef _WIN32 static int s_pwd_dot(char *myself, dev_t dev, ino_t ino, int height) { struct stat dotdot; struct stat dot; int ret; char parent[MAX_PATH_SZ * 3] = "../"; DIR *cwd = NULL; struct dirent *dir_p; char childname[MAX_PATH_SZ] = "???"; char full_path[MAX_LINE]; /* open the directory "myself" and look for the directory file name * that was "." in the child directory that called us * Keep this because we print it on the way out. * (If can't find use ??? I think). */ cwd = opendir(myself); if (cwd) { while ((dir_p = readdir(cwd)) != NULL) { snprintf(full_path, sizeof(full_path), "%s/%s", myself, dir_p->d_name); if (0 == (ret = stat(full_path, &dot))) { if ((dot.st_dev == dev) && (dot.st_ino == ino)) { strncpy(childname, dir_p->d_name, sizeof(childname)); break; } } } (void)closedir(cwd); } /* We will know when we have reached "root" when the .. is same as . * and same is denoted by same devno and inode */ if (0 != (ret = stat(myself, &dotdot))) { printf("[]"); return ret; } if ( (dotdot.st_dev != dev) || (dotdot.st_ino != ino)) { if (NULL == strncat(parent, myself, sizeof(parent) - 3)) { ret = 1; } else { ret = s_pwd_dot(parent, dotdot.st_dev, dotdot.st_ino, height+1); printf("/%s", childname); } } else { if (height == 0) printf("/"); } return ret; } static int s_pwd(int argc, char *argv[]) { struct stat dot; int ret; if (0 != (ret = stat(".", &dot))) return ret; ret = s_pwd_dot("..", dot.st_dev, dot.st_ino, 0); printf("\n"); return ret; } #else static int s_pwd(int argc, char *argv[]) { char cwd[MAX_LINE] = ""; if(GetCurrentDirectory(sizeof(cwd), cwd)){ printf("%s\n",cwd); return (0); } else { return(GetLastError()); } } #endif /* === quit */ static int s_quit(int argc, char *argv[]) { v_quit = 1; return 0; } #ifndef WIN32 /* ==== su */ static int s_su(int argc, char *argv[]) { int ret; uid_t uid; gid_t gid; char *usage[] = {"help", "su"}; if (argc != 3) { printf("Wrong number of arguments. Usage:\n"); s_help(2,usage); return (1); } if (0 == strcmp(argv[1], "uid")) { uid = atoi(argv[2]); ret = setuid(uid); } else if (0 == strcmp(argv[1], "euid")) { uid = atoi(argv[2]); ret = seteuid(uid); } else if (0 == strcmp(argv[1], "gid")) { gid = atoi(argv[2]); ret = setgid(gid); } else if (0 == strcmp(argv[1], "egid")) { gid = atoi(argv[2]); ret = setegid(gid); } else { printf("Invalid ID type '%s'. Usage:\n", argv[1]); s_help(2, usage); ret = 1; } return ret; } #endif /* WIN32 */ /* === ver */ static int s_ver(int argc, char *argv[]) { printf("%s\n", rcs); return 0; }