/* Copyright (C) 2005 Thomas Petazzoni, David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "fstest_utils.h" /** * @file shell.c * * Small shell. */ static int shell_uname (int argc, char *argv[]) { printf ("SOS article 10\n"); return 0; } static void shell_ls_internal(int detailed, int recursive, int reclevel) { char tab[256], *c; int i; struct dirent * dirent; DIR * here; here = opendir("."); if (! here) return; /* Build initial tabulation */ if (recursive) { for (c = tab, i = 0 ; (i < reclevel) && (i < sizeof(tab)/2) ; i++) { *c++ = ' '; *c++ = ' '; } *c++ = '\0'; } else *tab = '\0'; while ((dirent = readdir(here)) != NULL) { char entrychar; char * entrysuffix; switch(dirent->type) { case S_IFREG: entrychar='-'; entrysuffix=""; break; case S_IFDIR: entrychar='d'; entrysuffix="/"; break; case S_IFLNK: entrychar='l'; entrysuffix="@"; break; case S_IFCHR: entrychar='c'; entrysuffix=NULL; break; case S_IFBLK: entrychar='b'; entrysuffix=NULL; break; default: entrychar='?'; entrysuffix="?!?"; break; } if (detailed) { struct stat stat; char target_name[SOS_FS_DIRENT_NAME_MAXLEN]; char majorminor[24]; if (lstat(dirent->name, & stat)) continue; *target_name = '\0'; if (stat.st_type == S_IFLNK) { int fd = open(dirent->name, O_RDONLY | O_NOFOLLOW); if (fd >= 0) { int len = read(fd, target_name, sizeof(target_name) - 1); if (len < 0) *target_name='\0'; else target_name[len] = '\0'; close(fd); } } else if ( (stat.st_type == S_IFCHR) || (stat.st_type == S_IFBLK) ) { snprintf(majorminor, sizeof(majorminor), " %d,%d", stat.st_rdev_major, stat.st_rdev_minor); entrysuffix = majorminor; } printf("%s%c%c%c%c %lld %s%s%s%s (location=0x%llx)\n", tab, entrychar, (stat.st_access_rights&S_IRUSR)?'r':'-', (stat.st_access_rights&S_IWUSR)?'w':'-', (stat.st_access_rights&S_IXUSR)?'x':'-', stat.st_size, dirent->name, entrysuffix, (stat.st_type==S_IFLNK)?" -> ":"", target_name, stat.st_storage_location); } else printf("%s%s%s\n", tab, dirent->name, entrysuffix); /* Next iteration */ if (recursive) { int fd_here = dirfd(here); if (chdir(dirent->name)) continue; shell_ls_internal(detailed, recursive, reclevel+1); if(fchdir(fd_here)) { closedir(here); return; } } } closedir(here); } static void shell_ls(const char * path, int detailed, int recursive) { int fd_here = open(".", O_RDONLY | O_DIRECTORY); if (fd_here < 0) return; if (chdir(path)) { close(fd_here); return; } shell_ls_internal(detailed, recursive, 1); fchdir(fd_here); close(fd_here); } static int shell_chdir(int argc, char *argv[]) { int ret; ret = chdir(argv[1]); if (ret < 0) printf("Cannot chdir '%s': %d\n", argv[1], ret); return 0; } static int shell_touch (int argc, char *argv[]) { return creat (argv[1], S_IRUSR | S_IWUSR); } static int shell_mkdir (int argc, char *argv[]) { int retval; retval = mkdir (argv[1], S_IRUSR | S_IWUSR | S_IXUSR); if (retval != 0) printf("Cannot mkdir %s (%d)\n", argv[1], retval); return retval; } static int shell_rmdir (int argc, char *argv[]) { int ret; ret = rmdir (argv[1]); if (ret < 0) printf("Cannot rmdir '%s': %d\n", argv[1], ret); return 0; } static int shell_cat (int argc, char *argv[]) { int fd; char buf[4096]; int len; fd = open (argv[1], O_RDONLY); if (fd < 0) { printf ("Cannot open '%s'\n", argv[1]); return -1; } while (1) { len = read (fd, buf, sizeof(buf)-1); if (len <= 0) break; buf[len] = '\0'; printf ("%s", buf); } close (fd); return 0; } static int shell_edit (int argc, char *argv[]) { int fd; char buf[16]; int len; fd = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { printf ("Cannot create '%s': %d\n", argv[1], fd); return -1; } /* Activate echo and activate again canonical mode */ ioctl (0, SOS_IOCTL_TTY_SETPARAM, SOS_IOCTLPARAM_TTY_ECHO); ioctl (0, SOS_IOCTL_TTY_SETPARAM, SOS_IOCTLPARAM_TTY_CANON); while (1) { len = read (0, buf, sizeof(buf)); if (len <= 1) break; bochs_printf ("Writing %d bytes\n", len); len = write (fd, buf, len); if (len <= 0) { printf ("Cannot write to '%s': %d\n", argv[1], len); break; } } /* Desactivate echo and remove canonical mode */ ioctl (0, SOS_IOCTL_TTY_RESETPARAM, SOS_IOCTLPARAM_TTY_ECHO); ioctl (0, SOS_IOCTL_TTY_RESETPARAM, SOS_IOCTLPARAM_TTY_CANON); close (fd); return 0; } static int shell_hexdump (int argc, char *argv[]) { int fd; char buf[16]; int count = 0; int len; fd = open (argv[1], O_RDONLY); if (fd < 0) { printf ("Cannot open '%s': %d\n", argv[1], fd); return -1; } while (1) { int i; len = read (fd, buf, sizeof(buf)); if (len <= 0) break; if (count < 0x10) printf ("00%x ", count); else if (count < 0x100) printf ("0%x ", count); else if (count < 0x1000) printf ("%x ", count); for (i = 0; i < len; i++) { if (buf[i] < 0x10) printf ("0%x ", buf[i]); else printf ("%x ", buf[i]); } printf ("\n"); count += len; } close (fd); return 0; } static int shell_test (int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) { printf ("argv[%d] = %s\n", i, argv[i]); } return 0; } static int shell_spawn (int argc, char *argv[]) { int retval; if (argc < 2) { printf("Usage: spawn prog_name\n"); return -1; } retval = fork(); if (retval > 0) return 0; /* father returns */ if (retval < 0) { printf("Could not fork (%d)\n", retval); return -1; } printf("Created new process %d\n", getpid()); retval = execv(argv[1], & argv[1]); printf("Could not exec %s: error %d\n", argv[1], retval); exit(0); return 0; } static int shell_rm (int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) { int ret; ret = unlink (argv[i]); if (ret < 0) printf ("Cannot remove '%s', %d\n", argv[i], ret); } return 0; } static int shell_dd (int argc, char *argv[]) { char *infile = NULL; char *outfile = NULL; long bs = 1; long count = -1; int i; int infd, outfd; char *buffer; int ret; for (i = 1; i < argc; i++) { if (strncmp (argv[i], "if=", 3) == 0) infile = argv[i] + 3; else if (strncmp (argv[i], "of=", 3) == 0) outfile = argv[i] + 3; else if (strncmp (argv[i], "bs=", 3) == 0) { char *tmp = argv[i] + 3; char *multiplier_char; int multiplier = 1; int j; multiplier_char = & tmp[strlen(tmp)-1]; if (*multiplier_char == 'k' || *multiplier_char == 'K') { multiplier = 1024; *multiplier_char = '\0'; } else if (*multiplier_char == 'm' || *multiplier_char == 'M') { multiplier = 1024 * 1024; *multiplier_char = '\0'; } for (j = 0; j < strlen(tmp); j++) if (! isdigit(tmp[j])) { printf ("Syntax error in block size\n"); return -1; } bs = atol(tmp) * multiplier; } else if (strncmp (argv[i], "count=", 6) == 0) { char *tmp = argv[i] + 6; int j; for (j = 0; j < strlen(tmp); j++) if (! isdigit(tmp[j])) { printf ("Syntax error in block count\n"); return -1; } count = atol(tmp); } else { printf ("Syntax error\n"); return -1; } } infd = open(infile, O_RDONLY); if (infd < 0) { printf ("Cannot open '%s', %d\n", infile, infd); return infd; } outfd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (outfd < 0) { printf ("Cannot open '%s', %d\n", outfile, outfd); return outfd; } buffer = malloc (bs); if (! buffer) return -1; for (i = 0; (count < 0) || (i < count) ; i++) { int len; ret = read (infd, buffer, bs); if (ret < 0) { printf ("Input error from '%s' at %d\n", infile, i); return -1; } if (ret == 0) break; len = ret; ret = write (outfd, buffer, len); if (ret != len) { printf ("Output error to '%s' at %d (%d)\n", outfile, i, ret); return -1; } } return 0; } static int shell_partdump (int argc, char *argv[]) { /** * This structure defines the structure of a partition entry * (determined by the PC architecture) */ struct partition_entry { unsigned char active; unsigned char start_dl; unsigned short start_cylinder; unsigned char type; unsigned char end_dl; unsigned short end_cylinder; unsigned long lba; unsigned long size; }; /** * Offset of the partition table inside the 512-byte sector. */ #define PART_TABLE_OFFSET 446 /** * The most common partition types. For a complete list, you can for * example refer to * http://www.win.tue.nl/~aeb/partitions/partition_types-1.html */ #define PART_TYPE_EXTENDED 0x5 #define PART_TYPE_FAT16_1 0xe #define PART_TYPE_FAT16_2 0x6 #define PART_TYPE_FAT32_1 0xb #define PART_TYPE_FAT32_2 0xc #define PART_TYPE_LINUX_SWAP 0x82 #define PART_TYPE_LINUX 0x83 /** * Converts a partition type to a string */ const char * sos_part_type_str (unsigned int type) { switch (type) { case PART_TYPE_EXTENDED: return "Extended"; case PART_TYPE_FAT16_1: case PART_TYPE_FAT16_2: return "FAT16"; case PART_TYPE_FAT32_1: case PART_TYPE_FAT32_2: return "FAT32"; case PART_TYPE_LINUX_SWAP: return "Linux Swap"; case PART_TYPE_LINUX: return "Linux"; default: return "Unknown"; } } int fd; /* We assume the block size is 512 bytes. The clean way would be to have a BLKGETSIZE ioctl() on block devices */ #define BLKSIZE 512 char buffer[BLKSIZE]; int ret; struct partition_entry *part_entry; int partnum; unsigned int extstart = 0, extsup = 0; if (argc != 2) { printf ("Usage: partdump /dev/device\n"); return -1; } fd = open (argv[1], O_RDONLY); if (fd < 0) { printf ("Cannot open '%s', %d\n", argv[1], fd); return -1; } ret = read (fd, buffer, sizeof(buffer)); if (ret != sizeof(buffer)) { printf ("Read error in '%s', %d\n", argv[1], fd); return -1; } part_entry = (struct partition_entry *) (buffer + PART_TABLE_OFFSET); printf ("Partitions in %s:\n", argv[1]); /* Handle primary partitions */ for (partnum = 0; partnum < 4; partnum++) { if (part_entry [partnum].size == 0) continue; if (part_entry [partnum].type == PART_TYPE_EXTENDED) { if (extstart != 0) printf ("Warning: two extended partitions detected\n"); extstart = part_entry [partnum].lba; continue; } /* When size is >= 1 Mb, display size in Mb, otherwise display size in Kb */ if (part_entry[partnum].size * BLKSIZE >= (1024*1024)) printf (" - %s%d (primary, %lu Mb, %s)\n", argv[1], partnum+1, (part_entry[partnum].size * BLKSIZE >> 20), sos_part_type_str(part_entry[partnum].type)); else printf (" - %s%d (primary, %lu Kb, %s)\n", argv[1], partnum+1, (part_entry[partnum].size * BLKSIZE >> 10), sos_part_type_str(part_entry[partnum].type)); } while (extstart != 0) { ret = lseek (fd, (extstart + extsup) * BLKSIZE, SEEK_SET); if (ret != (extstart + extsup) * BLKSIZE) { printf ("Cannot seek at position %d in '%s', %d\n", (extstart + extsup) * BLKSIZE, argv[1], ret); return -1; } ret = read (fd, buffer, BLKSIZE); if (ret != BLKSIZE) { printf ("Read error in '%s', %d\n", argv[1], ret); break; } /* When size is >= 1 Mb, display size in Mb, otherwise display size in Kb */ if (part_entry[0].size * BLKSIZE >= (1024*1024)) printf (" - %s%d (logical volume, %lu Mb, %s)\n", argv[1], partnum+1, (part_entry[0].size * BLKSIZE >> 20), sos_part_type_str(part_entry[0].type)); else printf (" - %s%d (logical volume, %lu Kb, %s)\n", argv[1], partnum+1, (part_entry[0].size * BLKSIZE >> 10), sos_part_type_str(part_entry[0].type)); extsup = part_entry[1].lba; partnum ++; if (extsup == 0) break; } return 0; } static int shell_mount (int argc, char *argv[]) { /*char * end = NULL;*/ int retval = 0; int flags = 0; if (strncmp(argv[1], "-t", 2)) { /*flags = strtol(argv[1], &end, 10);*/ if (argc == 8) { retval = mount(argv[4], argv[5], argv[3], flags, argv[7]); } else { if (argc == 6) { retval = mount(argv[4], argv[5], argv[3], flags, NULL); } else { printf("mount [FLAGS] -t TYPE DEVICE MOUNT_DIR [-o ARGS]\n"); } } } else { if (argc == 7) { retval = mount(argv[3], argv[4], argv[2], 0, argv[6]); } else { if (argc == 5) { retval = mount(argv[3], argv[4], argv[2], 0, NULL); } else { printf("mount [FLAGS] -t TYPE DEVICE MOUNT_DIR [-o ARGS]\n"); } } } if (retval != 0) printf("Cannot mount %s (%d)\n", argv[2], retval); return retval; } static int shell_umount (int argc, char *argv[]) { int retval = 0; if (argc == 2) retval = umount(argv[1]); else printf("umount DIR\n"); if (retval != 0) printf("Cannot umount %s (%d)\n", argv[1], retval); return retval; } static int shell_mmap (int argc, char *argv[]) { /* * test mmap */ int fd, ret; /* Common use: shared file suppressed as soon as possible */ fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fork() == 0) { char *shrd; shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd children error\n"); nanosleep(1, 0); strzcpy(shrd, "Hello1 from the child (shared mapping) !", 4096); exit(0); } else { char *shrd; shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd father error\n"); strzcpy(shrd, "Garbage garbage garbage", 256); nanosleep(2, 0); bochs_printf("Father woken up\n"); bochs_printf("Read string from child: '%s'\n", shrd); if (strcmp(shrd, "Hello1 from the child (shared mapping) !") == 0) bochs_printf("TEST1 OK\n"); munmap(shrd, 8192); } ret = close(fd); if (ret == 0) bochs_printf("close OK\n"); ret = unlink("mmap.txt"); if (ret == 0) bochs_printf("delete mmap.txt OK\n"); shell_ls_internal(1, 0, 1); /* Common use: shared file suppressed as soon as possible */ fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); ret = unlink("mmap.txt"); if (ret == 0) bochs_printf("delete mmap.txt OK 2\n"); if (fork() == 0) { char *shrd; shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd children error\n"); nanosleep(1, 0); strzcpy(shrd, "Hello2 from the child (shared mapping) !", 4096); exit(0); } else { char *shrd; shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd father error\n"); strzcpy(shrd, "Garbage garbage garbage", 256); nanosleep(2, 0); bochs_printf("Father woken up\n"); bochs_printf("Read string from child: '%s'\n", shrd); if (strcmp(shrd, "Hello2 from the child (shared mapping) !") == 0) bochs_printf("Test 2 OK\n"); /*msync(shrd, 4096, MAP_SHARED);*/ munmap(shrd, 8192); } ret = close(fd); if (ret == 0) bochs_printf("close OK\n"); shell_ls_internal(1, 0, 1); /* Basic use */ ret = creat("mmap.txt", S_IRUSR | S_IWUSR); if (ret == 0) bochs_printf("creat OK\n"); if (fork() == 0) { char *shrd; fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd children error\n"); nanosleep(1, 0); strzcpy(shrd, "Hello3 from the child (shared mapping) !", 4096); exit(0); } else { char *shrd; fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096); if (shrd == NULL) bochs_printf("shrd father error\n"); strzcpy(shrd, "Garbage garbage garbage", 256); nanosleep(2, 0); bochs_printf("Father woken up\n"); bochs_printf("Read string from child: '%s'\n", shrd); if (strcmp(shrd, "Hello3 from the child (shared mapping) !") == 0) bochs_printf("TEST3 OK\n"); munmap(shrd, 8192); ret = close(fd); if (ret == 0) bochs_printf("close OK\n"); } shell_ls_internal(1, 0, 1); return 0; } void command_exec (char * cmd) { char *c; int argc = 1, i; char **argv; for (c = cmd; *c != '\0'; c++) { if (*c == ' ') { /* Skip all spaces */ while (*c && *c == ' ') c++; /* Reached the end of the command line ? */ if (! *c) break; argc++; } } argv = malloc ((argc+1) * sizeof(char*)); argv[argc] = NULL; /* To be compatible with execv(e) */ if (argv == NULL) return; for (i = 0, c = cmd; i < argc; i++) { argv[i] = c; while (*c != ' ' && *c != '\0') c++; *c = '\0'; c++; /* Skip spaces */ while (*c && *c == ' ') c++; } if (! strcmp (argv[0], "uname")) shell_uname(argc, argv); else if (! strcmp (argv[0], "ls")) { if (argv[1]) shell_ls (argv[1], 1, 0); else shell_ls (".", 1, 0); } else if (! strcmp (argv[0], "cd")) { shell_chdir(argc, argv); } else if (! strcmp (argv[0], "touch")) { shell_touch (argc, argv); } else if (! strcmp (argv[0], "mkdir")) { shell_mkdir (argc, argv); } else if (! strcmp (argv[0], "rmdir")) { shell_rmdir (argc, argv); } else if (! strcmp (argv[0], "cat")) { shell_cat (argc, argv); } else if (! strcmp (argv[0], "edit")) { shell_edit (argc, argv); } else if (! strcmp (argv[0], "hexdump")) { shell_hexdump (argc, argv); } else if (! strcmp (argv[0], "test")) { shell_test (argc, argv); } else if (! strcmp (argv[0], "spawn")) { shell_spawn (argc, argv); } else if (! strcmp (argv[0], "dd")) { shell_dd (argc, argv); } else if (! strcmp (argv[0], "rm")) { shell_rm (argc, argv); } else if (! strcmp (argv[0], "partdump")) { shell_partdump (argc, argv); } else if (! strcmp (argv[0], "mount")) { shell_mount (argc, argv); } else if (! strcmp (argv[0], "umount")) { shell_umount (argc, argv); } else if (! strcmp (argv[0], "mmap")) { shell_mmap (argc, argv); } else if (! strcmp(argv[0], "getenv")) { if (argc > 1) printf("%s\n", getenv(argv[1])); else printf("Variable name missing\n"); } else if (! strcmp(argv[0], "setenv")) { if (argc > 2) printf("retval=%d\n", setenv(argv[1], argv[2], TRUE)); else printf("Variable name/value missing\n"); } else if (! strcmp(argv[0], "unsetenv")) { if (argc > 1) unsetenv(argv[1]); else printf("Variable name missing\n"); } else printf ("Command not found\n"); free (argv); } int main(void) { char buffer[256]; int i; char chr; ioctl (0, SOS_IOCTL_TTY_RESETPARAM, SOS_IOCTLPARAM_TTY_CANON); while (1) { memset (buffer, 0, sizeof(buffer)); i = 0; printf ("$ "); while (1) { read (0, & chr, 1); if (chr == '\n') { buffer[i] = '\0'; printf ("\n"); if (i != 0) command_exec (buffer); break; } else if (chr == '\b') { if (i > 0) { i--; printf ("\b"); } } else if (i < 256) { buffer[i++] = chr; printf ("%c", chr); } else printf ("%c", chr); } } return 0; }