Skip to content

Instantly share code, notes, and snippets.

@alirezaarzehgar
Created October 12, 2022 14:20
Show Gist options
  • Select an option

  • Save alirezaarzehgar/0f1e121c2cef14f609c8cd2e94767d7c to your computer and use it in GitHub Desktop.

Select an option

Save alirezaarzehgar/0f1e121c2cef14f609c8cd2e94767d7c to your computer and use it in GitHub Desktop.
Simple educational shell
/**
* @file shell.c
* @author alirezaarzehgar (alirezaarzehgar82@gmail.com)
* @brief
* @version 0.1
* @date 2022-10-12
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <wait.h>
#include <pwd.h>
#include <grp.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <wordexp.h>
int ls_main(int argc, char **argv);
int cd_main(int argc, char **argv);
int pwd_main(int argc, char **argv);
int umask_main(int argc, char **argv);
int clear_main(int argc, char **argv);
int exit_main(int argc, char **argv);
uid_t uid = 0;
char cwd[PATH_MAX] = {0}, cmd[BUFSIZ] = {0};
struct builtin_t
{
char name[5];
int (*func)(int argc, char **argv);
} builtins[] = {
{.name = "ls", .func = ls_main},
{.name = "cd", .func = cd_main},
{.name = "pwd", .func = pwd_main},
{.name = "umask", .func = umask_main},
{.name = "clear", .func = clear_main},
{.name = "exit", .func = exit_main},
{},
};
int main(int argc, char const *argv[])
{
uid = getuid();
if (!getcwd(cwd, PATH_MAX))
perror("getcwd");
printf("%s%c ", cwd, !uid ? '#' : '>');
while (fgets(cmd, BUFSIZ, stdin))
{
pid_t pid = 0, status = 0;
wordexp_t we;
struct builtin_t *bi;
if (cmd[strlen(cmd) - 1] == '\n')
cmd[strlen(cmd) - 1] = 0;
if (wordexp(cmd, &we, 0))
perror("wordexp");
if (!we.we_wordc)
goto end;
for (bi = builtins; bi->func; bi++)
if (strlen(we.we_wordv[0]) == strlen(bi->name) &&
!strncmp(we.we_wordv[0], bi->name, strlen(we.we_wordv[0])))
{
bi->func(we.we_wordc, we.we_wordv);
goto end;
}
if ((pid = fork()) < 0)
perror("fork");
else if (!pid)
{
status = execvp(we.we_wordv[0], we.we_wordv);
if (status)
fprintf(stderr, "shell: Unknown command: %s\n", we.we_wordv[0]);
}
waitpid(pid, &status, 0);
end:
wordfree(&we);
printf("%s%c ", cwd, !uid ? '#' : '>');
}
return (EXIT_SUCCESS);
}
char *ls_color(mode_t m)
{
char *color = "";
if ((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH))
color = "\033[32m";
if (S_ISDIR(m))
color = "\033[34m";
if ((m & S_ISUID) || (m & S_ISGID))
color = "\033[41m";
if (S_ISLNK(m))
color = "\033[36m";
if (S_ISCHR(m))
color = "\033[33;40m";
return color;
}
void ls_show(char *path, char *name, struct stat st)
{
int fd = 0;
mode_t m = st.st_mode, lm = 0;
char x = 0, *extra = "", lname[PATH_MAX] = {0};
struct passwd *upwd;
struct group *grp;
struct stat slt;
if (!(upwd = getpwuid(st.st_uid)))
perror("getpwuid");
if (!(grp = getgrgid(st.st_gid)))
perror("getgrgid");
if (S_ISDIR(m))
{
putchar('d');
extra = "\033[0m/";
}
else if (S_ISLNK(m))
{
if (!(fd = open(path, O_RDONLY)))
{
perror("open");
return;
}
if (readlinkat(fd, name, lname, PATH_MAX) < 0)
perror("readlink");
extra = "\033[0m -> ";
putchar('l');
close(fd);
}
else if (S_ISCHR(m))
putchar('c');
else
putchar('-');
x = (m & S_ISUID ? 's' : 'x');
putchar(m & S_IRUSR ? 'r' : '-');
putchar(m & S_IWUSR ? 'w' : '-');
putchar(m & S_IXUSR ? x : '-');
x = (m & S_ISGID ? 's' : 'x');
putchar(m & S_IRGRP ? 'r' : '-');
putchar(m & S_IWGRP ? 'w' : '-');
putchar(m & S_IXGRP ? x : '-');
putchar(m & S_IROTH ? 'r' : '-');
putchar(m & S_IWOTH ? 'w' : '-');
putchar(m & S_IXOTH ? 'x' : '-');
putchar(' ');
printf("%d %s\t%s\t", st.st_nlink, upwd->pw_name, grp->gr_name);
printf("%ld\t", st.st_size);
printf("%s%s\033[0m", ls_color(m), name);
printf("%s%s%s", extra, ls_color(lm), lname);
putchar('\n');
}
int ls_main(int argc, char **argv)
{
int fd = 0;
size_t i = 0;
struct stat st;
DIR *dp;
struct dirent *dep;
if (argc == 1)
strncpy(argv[0], ".", 2);
else
i++;
while (i < argc)
{
if (lstat(argv[i], &st))
{
perror(argv[i]);
goto end;
}
if (!S_ISDIR(st.st_mode))
{
ls_show("", argv[i], st);
goto end;
}
if (!(dp = opendir(argv[i])))
{
perror(argv[i]);
goto end;
}
while ((dep = readdir(dp)))
{
if (!(fd = open(argv[i], O_RDONLY)))
{
perror(argv[i]);
continue;
}
if (fstatat(fd, dep->d_name, &st, AT_SYMLINK_NOFOLLOW))
{
perror(dep->d_name);
continue;
}
ls_show(argv[i], dep->d_name, st);
close(fd);
}
closedir(dp);
end:
printf("\n");
i++;
}
return (EXIT_SUCCESS);
}
int cd_main(int argc, char **argv)
{
struct passwd *p;
char *path = argv[1];
if (argc > 2)
{
fprintf(stderr, "Too many args for %s command", argv[0]);
return (EXIT_FAILURE);
}
if (argc == 1)
{
if (!(p = getpwuid(getuid())))
{
perror("getpwuid");
return (EXIT_FAILURE);
}
path = p->pw_dir;
}
if (chdir(path))
{
perror(path);
return (EXIT_FAILURE);
}
if (!getcwd(cwd, PATH_MAX))
{
perror("getcwd");
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
int pwd_main(int argc, char **argv)
{
char pwd[PATH_MAX] = {0};
if (!getcwd(pwd, PATH_MAX))
{
perror("getcwd");
return (EXIT_FAILURE);
}
printf("%s\n", pwd);
return (EXIT_SUCCESS);
}
int umask_main(int argc, char **argv)
{
mode_t mask = 0;
mode_t masks[][3] = {
{0, 0, 0},
{S_IXUSR, S_IWUSR, S_IRUSR},
{S_IXGRP, S_IWGRP, S_IRGRP},
{S_IXOTH, S_IWOTH, S_IROTH},
};
if (argc > 2)
{
fprintf(stderr, "Too many args for %s command", argv[0]);
return (EXIT_FAILURE);
}
if (argc == 2)
{
int m = 3, i = strlen(argv[1]) - 1;
if (strlen(argv[1]) > 4)
{
fprintf(stderr, "%s: Invalid mask “%s”\n", argv[0], argv[1]);
return (EXIT_FAILURE);
}
mask = 0;
for (; i >= 0; i--, m--)
{
switch (argv[1][i])
{
case '1':
mask |= masks[m][0];
break;
case '2':
mask |= masks[m][1];
break;
case '4':
mask |= masks[m][2];
break;
}
}
umask(mask);
}
if (argc == 1)
{
mask = umask(0);
umask(mask);
putchar('0');
putchar(mask & S_IRUSR ? '4' : 0);
putchar(mask & S_IWUSR ? '2' : 0);
putchar(mask & S_IXUSR ? '1' : 0);
if (!(mask & S_IRUSR) && !(mask & S_IWUSR) & !(mask & S_IXUSR))
putchar('0');
putchar(mask & S_IRGRP ? '4' : 0);
putchar(mask & S_IWGRP ? '2' : 0);
putchar(mask & S_IXGRP ? '1' : 0);
if (!(mask & S_IRGRP) && !(mask & S_IWGRP) & !(mask & S_IXGRP))
putchar('0');
putchar(mask & S_IROTH ? '4' : 0);
putchar(mask & S_IWOTH ? '2' : 0);
putchar(mask & S_IXOTH ? '1' : 0);
if (!(mask & S_IROTH) && !(mask & S_IWOTH) & !(mask & S_IXOTH))
putchar('0');
putchar('\n');
}
return (EXIT_SUCCESS);
}
int clear_main(int argc, char **argv)
{
printf("\033[2J\033[0:0f");
return (EXIT_SUCCESS);
}
int exit_main(int argc, char **argv)
{
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment