sos-code-article10/userland/shell.c

1002 lines
21 KiB
C

/* 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 <crt.h>
#include <libc.h>
#include <stdarg.h>
#include <string.h>
#include <debug.h>
#include <drivers/devices.h>
#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;
}