UNIX Shell and History Feature - linux

how to add History Feature in unix shell to allows the user to access the most recently entered
command , The user will be able to access up to 10 commands by using the
feature.
This comment explain the history part from the project:
The user will be able to access up to 10 commands by using the
feature. The commands will be consecutively numbered starting at 1, and
the numbering will continue past 10. For example, if the user has entered 35
commands, the 10 most recent commands will be numbered 26 to 35.
The userwill be able to list the command history by entering the command
history
at the osh> prompt. As an example, assume that the history consists of the
commands (from most to least recent):
ps, ls -l, top, cal, who, date
The command history will output:
6 ps
5 ls -l
4 top
3 cal
2 who
1 date
Your program should support two techniques for retrieving commands
from the command history:
1. When the user enters !!, the most recent command in the history is
executed.
2. When the user enters a single ! followed by an integer N, the Nth
command in the history is executed.
this is my code that including the history part, but i have error and dont know how to fix it. please help
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LINE 80
char *history[10][MAX_LINE];
int po;
void setup(char inputBuffer[], char *args[],int *background)
{
int length,
i,
start,
ct;
ct = 0;
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0);
if (length < 0){
perror("error ");
exit(-1);
}
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' :
if(start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
start = -1;
break;
case '\n':
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL;
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default :
if (start == -1)
start = i;
}
}
args[ct] = NULL;
}
int main(void)
{
char inputBuffer[MAX_LINE];
int background;
char *args[MAX_LINE/2+1];
while (1){
background = 0;
printf("os>");
fflush(0);
setup(inputBuffer, args, &background);
/**
* After reading user input, the steps are:
* (1) fork a child process using fork()
* (2) the child process will invoke execvp()
* (3) if command included &, parent will invoke wait()
*/
pid_t pid = fork();
printf("Fork created.\n");
/*
For example, if the
user enters the command ps -ael at the osh> prompt, the values stored in the
args array are:
args[0] = "ps"
args[1] = "-ael"
args[2] = NULL
This args array will be passed to the execvp() function, which has the
following prototype:
execvp(char *command, char *params[]);
*/
if(pid < 0){
printf("Fork failed.\n");
}else if(pid == 0){
if( strcmp(args[0],"history") == 0){ /* Print History */
displayHistory();
}else if(strcmp(args[0],"r") == 0){ /* r num */
int index = (int) args[1];
/*runHistoryAt( index - 1);*/
}else if(strcmp(args[0],"rr") == 0){ /* Run recent */
/*runHistoryAt(0);*/
}else{ /* Execute normally */
printf("executing..., adding to history buffer\n");
/* Add args to history buffer */
int j;
for (j = 0; j < sizeof(args); j++) {
history[po][j] = args[j];
}
po = (po + 1) % 10;
/* Execute! */
execvp(args[0],args);
}
}
if(background == 0){
wait(NULL);
}else{
setup(inputBuffer, args, &background);
}
}
}

I would use the GNU readline library. It gives you line edition and history support and you can also have completion.

Related

Problems with ptrace(PTRACE_ME,...) and subsequent wait

I am porting a debugger, 'pi' ('process inspector') to Linux and am
working on the code for fork/exec of a child to inspect it. I am
following standard procedure (I believe) but the wait is hanging.
'hang' is the procedure which does the work, the 'cmd' argument being
the name of the binary (a.out) to trace:
int Hostfunc::hang(char *cmd){
char *argv[10], *cp;
int i;
Localproc *p;
struct exec exec;
struct rlimit rlim;
i = strlen(cmd);
if (++i > sizeof(procbuffer)) {
i = sizeof(procbuffer) - 1;
procbuffer[i] = 0;
}
bcopy(cmd, procbuffer, i);
argv[0] = cp = procbuffer;
for(i = 1;;) {
while(*cp && *cp != ' ')
cp++;
if (!*cp) {
argv[i] = 0;
break;
} else {
*cp++ = 0;
while (*cp == ' ')
cp++;
if (*cp)
argv[i++] = cp;
}
}
hangpid = fork();
if (!hangpid){
int fd, nfiles = 20;
if(getrlimit(RLIMIT_NOFILE, &rlim))
nfiles = rlim.rlim_cur;
for( fd = 0; fd < nfiles; ++fd )
close(fd);
open("/dev/null", 2);
dup2(0, 1);
dup2(0, 2);
setpgid(0, 0);
ptrace(PTRACE_TRACEME, 0, 0, 0);
execvp(argv[0], argv);
exit(0);
}
if (hangpid < 0)
return 0;
p = new Localproc;
if (!p) {
kill(9, hangpid);
return 0;
}
p->sigmsk = sigmaskinit();
p->pid = hangpid;
if (!procwait(p, 0)) {
delete p;
return 0;
}
if (p->state.state == UNIX_BREAKED)
p->state.state = UNIX_HALTED;
p->opencnt = 0;
p->next = phead;
phead = p;
return hangpid;
}
I put the 'abort()' in to catch a non-zero return from ptrace,
but that is not happening. The call to 'raise' seems to be a
common practice but a cursory look at gdb's code reveals it is
not used there. In any case it makes no difference to the outcome.
`procwait' is as follows:
int Hostfunc::procwait(Localproc *p, int flag){
int tstat;
int cursig;
again:
if (p->pid != waitpid(p->pid, &tstat, (flag&WAIT_POLL)? WNOHANG: 0))
return 0;
if (flag & WAIT_DISCARD)
return 1;
if (WIFSTOPPED(tstat)) {
cursig = WSTOPSIG(tstat);
if (cursig == SIGSTOP)
p->state.state = UNIX_HALTED;
else if (cursig == SIGTRAP)
p->state.state = UNIX_BREAKED;
else {
if (p->state.state == UNIX_ACTIVE &&
!(p->sigmsk&bit(cursig))) {
ptrace(PTRACE_CONT, p->pid, 1, cursig, 0);
goto again;
}
else {
p->state.state = UNIX_PENDING;
p->state.code = cursig;
}
}
} else {
p->state.state = UNIX_ERRORED;
p->state.code = WEXITSTATUS(tstat) & 0xFFFF;
}
return 1;
}
The 'waitpid' in 'procwait' just hangs. If I run the program with
the above code, and run a 'ps', I can see that 'pi' has forked
but hasn't yet called exec, because the command line is still
'pi', and not the name of the binary I am forking. I discovered
that if I remove the 'raise', 'pi' still hangs but 'ps' now
shows that the forked program has the name of the binary being
examined, which suggests it has performed the exec.
So, as far as I can see, I am following documented procedures to
take control of a forked process but it isn't working.
Noel Hunt
I have found the problem (with my own code, as Nate pointed out), but the cause was obscure until I ran 'strace pi'. It was clear from that that there was a SIGCHLD handler, and it was executing a wait. The parent enters wait, SIGCHLD is delivered, the handler waits and thus reaping the status of the child, then wait is restarted in the parent and hangs because there is no longer any change of state. The SIGCHLD handler makes sense because the pi wants to be informed of state changes in the child. The first version of 'pi' I got working was a Solaris version, and it uses /proc for process control so there was no use of 'wait' to get child status, hence I didn't see this problem in the Solaris version.

U-boot does not silence its output

I have this uboot
VERSION = 2017
PATCHLEVEL = 03
I am trying to silent the console using the silent variable.I defined this #define CONFIG_SILENT_CONSOLE
So at boot time I am interrupting the console, and entering
setenv silent 1
save
reset
Now after reset, or power on reset I try again get console logs.After seeing env variables
printenv
I see my saved variable correctly in env varibles
silent=1
but still u-boot is not silent. I suspect this function is failing at checking for this env variable,
char *getenv(const char *name)
{
if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */
ENTRY e, *ep;
WATCHDOG_RESET();
e.key = name;
e.data = NULL;
hsearch_r(e, FIND, &ep, &env_htab, 0); /*this function is maybe returning*/
return ep ? ep->data : NULL;
}
/* restricted capabilities before import */
if (getenv_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0)
return (char *)(gd->env_buf);
return NULL;
}
But what exactly is happening?
Is there something like before relocation time env variables and after relocation env variables because the function,
static void console_update_silent(void)
{
#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL){
puts("silent");
gd->flags |= GD_FLG_SILENT;
}
else{
puts("Non silent");
gd->flags &= ~GD_FLG_SILENT;
}
#endif
}
/* Called before relocation - use serial functions */
int console_init_f(void)
{
gd->have_console = 1;
console_update_silent();
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);
return 0;
}
console_init_f says its before relocation.
I have put some prints to see and always gets non silent, even if I have saved the silent variable,
I am using a sd card to boot(mmc), I don't have any debugger, so I
I tried printing default environment, as
env print default
## Error: "default" not defined
So there is not default environment too.
Any tips or help will make me understand.
P.S.
I explicitly defined silent in #define CONFIG_EXTRA_ENV_SETTINGS
Now u-boot is silent.
Doing a setenv silent should remove this from env variable, and I can see that its gone, but still on reboot my uboot is silent.
So something about environment variable is clearly mystery to me.
P.P.S
I come to see this code,
int getenv_f(const char *name, char *buf, unsigned len)
{
int i, nxt;
for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
int val, n;
for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) {
if (nxt >= CONFIG_ENV_SIZE)
return -1;
}
val = envmatch((uchar *)name, i);
if (val < 0)
continue;
/* found; copy out */
for (n = 0; n < len; ++n, ++buf) {
*buf = env_get_char(val++);
if (*buf == '\0')
return n;
}
if (n)
*--buf = '\0';
printf("env_buf [%d bytes] too small for value of \"%s\"\n",
len, name);
return n;
}
return -1;
}
Which is called by this
char *getenv(const char *name)
{
if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */
ENTRY e, *ep;
WATCHDOG_RESET();
e.key = name;
e.data = NULL;
hsearch_r(e, FIND, &ep, &env_htab, 0);
return ep ? ep->data : NULL;
}
/* restricted capabilities before import */
if (getenv_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0)
return (char *)(gd->env_buf);
return NULL;
}
From early board_init_f
this function
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
makes env_addr to point always to read only memory of the code, and does not point to anything else such as where mmc environment are saved.
So this function always point to default_environment variable.
__weak uchar env_get_char_spec(int index)
{
return *((uchar *)(gd->env_addr + index));
}
static uchar env_get_char_init(int index)
{
/* if crc was bad, use the default environment */
if (gd->env_valid)
return env_get_char_spec(index);
else
return default_environment[index];
}
uchar env_get_char_memory(int index)
{
return *env_get_addr(index);
}
uchar env_get_char(int index)
{
/* if relocated to RAM */
if (gd->flags & GD_FLG_RELOC)
return env_get_char_memory(index);
else
return env_get_char_init(index);
}
So I conclude that inherently, u-boot code there is no possibility to point the mmc area where environments are stored.
Can anyone confirm this?

shebang for custom shell?

I'm writing a custom shell and I want it to execute a script:
if [ type less > /dev/null ];then PAGER=less; fi
echo $PAGER
printenv|grep $1|$PAGER
It works if I run it from the bash and with my custom shell:
$ ./shell -f ../checkenv.sh GNOME
[13607]
[13606]
GNOME_KEYRING_CONTROL=
GNOME_KEYRING_PID=
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
INSTANCE=GNOME
XDG_CURRENT_DESKTOP=GNOME
(END)
But if I start my shell and then try and run the script, I get an error message.
$ ./shell
'PATH' is set to /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin.
$ ../checkenv.sh GNOME
14786: executing ../checkenv.sh
../checkenv.sh: 2: [: type: unexpected operator
14786: executed
$
This seems to be because I don't have a shebang, but I don't know how to use a shebang for a custom shell. Should I install my custom shell in /usr/bin/ or make some other arrangement?
My main function and my readline function are:
int main(int argc, char *argv[]) {
bool donotrun = false;
struct sigaction new_action, old_action;
hashtable_t *hashtable = ht_create(65536);
/* Set up the structure to specify the new action. */
new_action.sa_handler = termination_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
sigaction(SIGINT, &new_action, NULL);
sigaction(SIGHUP, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
sigaction(SIGHUP, &new_action, NULL);
sigaction(SIGTERM, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
sigaction(SIGTERM, &new_action, NULL);
bool background = false;
int index = 0;
int i;
char *cvalue = NULL;
const char *commandFile = NULL;
while (1) {
index = 0;
i = getopt_long(argc, argv, "pc:fvh",
options, &index);
if (i == -1)
break;
switch (i) {
case 'p': {
exit(EXIT_SUCCESS);
}
case 'v': {
printf("sh OpenShell version 0.1(a)\n");
printf("Version: %s\n", VERSION);
// printf ("%s / %s / %s / %s\n",
// program_name, version,
// build_date, build_git_sha);
exit(EXIT_SUCCESS);
}
case 'h': {
usage();
exit(EXIT_SUCCESS);
}
case 'c': {
cvalue = optarg;
command(cvalue, hashtable, background);
exit(EXIT_SUCCESS);
}
case 'f': {
/*
* Execute commands from file.
* This is used for osh script files.
* The quiet flag is also set.
*/
//if ((argc != 1) || commandFile)
//usage();
//quietFlag = TRUE;
printf("case f\n");
//commandFile = *argv++;
argc--;
*argv++;
*argv++;
readFile(*argv++, argc, argv, hashtable, background);
//free(line);
exit(0);
//break;
}
case '?':
if (optopt == 'c')
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf(stderr,
"Unknown option character `\\x%x'.\n",
optopt);
default: {
return 1;
}
}
}
getPath();
char *copy = "";
for (; ;) {
bool scanning = true;
while (scanning) {
char *line = NULL;
line = readline("$ ");
if (line == NULL) {
/* No more lines, so exit the loop. */
break;
}
if (line)
copy = strdup(line);
if (line && !strstr(line, "for") && !strstr(line, "==") && !strstr(line, "if") && strstr(line, "=")) {
donotrun = true;
char str[128];
char *ptr;
strcpy(str, line);
strtok_r (str, "=", &ptr);
ht_set(hashtable, str, ptr);
}
if (!scanning)
break;
if (commandFile!=NULL || !isatty(fileno(stdin))) {
*argv++;
readFile(*argv++, argc, argv, hashtable, background);
free(line);
exit(0);
}
else {
if (!donotrun) {
line = strrep(line, " | ", "|");
line = strrep(line, " |", "|");
background = testFn2(line);
if (background)
line[strlen(line) - 1] = '\0';
command(line, hashtable, background);
}
donotrun = false;
add_history(copy);
}
free(copy);
}
}
// ParseFree(pParser, free);FIXME: where should this go?
return 0;
}
/*
* Read commands from the specified file.
* A null name pointer indicates to read from stdin.
*/
static int readFile(const char *name, int argc, char ** argv, hashtable_t *hashtable, bool background) {
FILE *fp;
int cc;
bool ttyFlag;
char buf[CMD_LEN];
int r = 0;
if (sourceCount >= MAX_SOURCE) {
fprintf(stderr, "Too many source files\n");
return 1;
}
fp = stdin;
printf("name %s\n", name);
if (name) {
fp = fopen(name, "r");
if (fp == NULL) {
perror(name);
return 1;
}
}
sourcefiles[sourceCount++] = fp;
ttyFlag = isatty(fileno(fp));
int i = 0;
while (true) {
if (ttyFlag)
showPrompt();
if (intFlag && !ttyFlag && (fp != stdin)) {
fclose(fp);
sourceCount--;
return 1;
}
if (fgets(buf, CMD_LEN - 1, fp) == NULL) {
if (ferror(fp) && (errno == EINTR)) {
clearerr(fp);
continue;
}
break;
}
cc = strlen(buf);
if (buf[cc - 1] == '\n')
cc--;
while ((cc > 0) && isBlank(buf[cc - 1]))
cc--;
buf[cc] = '\0';
//printf("buf %s\n", argv[0]);
strreplace(buf, "$1", argv[0]);
//printf("arg %s\n", ++argv);
if (strstr(buf, "=")) {
char str[128];
char *ptr;
strcpy(str, buf);
strtok_r (str, "=", &ptr);
ht_set(hashtable, str, ptr);
}
//printf("the command is %s\n", buf);
r = command(buf, hashtable, background);
i++;
}
if (ferror(fp)) {
perror("Reading command line");
if (fp == stdin)
exit(1);
}
clearerr(fp);
if (fp != stdin)
fclose(fp);
sourceCount--;
return r;
}
A shebang line simply specifies the full path to the interpreter, plus (optionally) an argument to be passed.
Apparently your custom shell requires a -f followed by the script name, followed by any arguments to be passed to the script.
So just add this as the first line of your script:
#!/path/to/shell -f
and make sure the script has execute permissions. Your shell doesn't have to be installed in /usr/bin; you just have to specify the full path on the #! line.
There's also a /usr/bin/env hack:
#!/usr/bin/env shell
but on many systems it doesn't permit passing an extra argument. (You might consider modifying your custom shell so it takes the script name as an argument without the -f.) I've discussed the pros and cons of #!/usr/bin/env in this answer.
Note that the #! mechanism is handled by the kernel, not by the shell.
You might simply remove the square brackets in your script test:
if type less > /dev/null ;then PAGER=less; fi
echo $PAGER
printenv|grep $1|$PAGER

Filesystem has been set read-only for clusters badly computed error

my sd cart run a long time in embed linux system,but sometime the filesystem set readonly and print bellow msg:
clusters badly computed (587 != 531)
FAT: Filesystem panic (dev hda6)
i see source code from linux kernel as show this: who can explain why this error,tks very much.
/*
* fat_chain_add() adds a new cluster to the chain of clusters represented
* by inode.
*/
int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int ret, new_fclus, last;
/*
* We must locate the last cluster of the file to add this new
* one (new_dclus) to the end of the link list (the FAT).
*/
last = new_fclus = 0;
if (MSDOS_I(inode)->i_start) {
int fclus, dclus;
ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
if (ret < 0)
return ret;
new_fclus = fclus + 1;
last = dclus;
}
/* add new one to the last of the cluster chain */
if (last) {
struct fat_entry fatent;
fatent_init(&fatent);
ret = fat_ent_read(inode, &fatent, last);
if (ret >= 0) {
int wait = inode_needs_sync(inode);
ret = fat_ent_write(inode, &fatent, new_dclus, wait);
fatent_brelse(&fatent);
}
if (ret < 0)
return ret;
/*
* FIXME:Although we can add this cache, fat_cache_add() is
* assuming to be called after linear search with fat_cache_id.
*/
// fat_cache_add(inode, new_fclus, new_dclus);
} else {
MSDOS_I(inode)->i_start = new_dclus;
MSDOS_I(inode)->i_logstart = new_dclus;
/*
* Since generic_write_sync() synchronizes regular files later,
* we sync here only directories.
*/
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
ret = fat_sync_inode(inode);
if (ret)
return ret;
} else
mark_inode_dirty(inode);
}
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
fat_fs_error(sb, "clusters badly computed (%d != %llu)",
new_fclus,
(llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
fat_cache_inval_inode(inode);
}
inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
return 0;
}

How should my program behave to handle errors?

I have written a program (with code from SO) that does printenv | sort | less and now I should implement error-handling. How can that be done? The program should fail gracefully, for example when passed the wrong arguments.
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
struct command
{
const char **argv;
};
/* Helper function that spawns processes */
int spawn_proc (int in, int out, struct command *cmd) {
pid_t pid;
if ((pid = fork ()) == 0) {
if (in != 0) {
dup2 (in, 0);
close (in);
}
if (out != 1) {
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
/* Helper function that forks pipes */
int fork_pipes (int n, struct command *cmd) {
int i;
int in, fd [2];
for (i = 0; i < n - 1; ++i) {
pipe (fd);
spawn_proc (in, fd [1], cmd + i);
close (fd [1]);
in = fd [0];
}
dup2 (in, 0);
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int main (int argc, char ** argv) {
int i;
if (argc == 1) { /* There were no arguments */
const char *printenv[] = { "printenv", 0};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {sort}, {less} };
return fork_pipes (3, cmd);
}
if (argc > 1) { /* I'd like an argument */
if (strncmp(argv[1], "cd", 2) && strncmp(argv[1], "exit", 2)) {
char *tmp;
int len = 1;
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2;
}
tmp = (char*) malloc(len);
tmp[0] = '\0';
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), argv[i]);
}
const char *printenv[] = { "printenv", 0};
const char *grep[] = { "grep", "-E", tmp, NULL};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {grep}, {sort}, {less} };
return fork_pipes (4, cmd);
free(tmp);
} else if (! strncmp(argv[1], "cd", 2)) { /* change directory */
printf("change directory to %s\n" , argv[2]);
chdir(argv[2]);
} else if (! strncmp(argv[1], "exit", 2)) { /* change directory */
printf("exit\n");
exit(0);
}
}
exit(0);
}
It's going to be frankly a bit painful to go through your program and fix all those missing-error-handling bugs after the fact. Much better would have been to write correct code from the start! Moreover, you have more bugs than just missing error handling. I didn't scan all of your code, but at first glance I already saw one use of an uninitialized local variable (in in fork_pipes is used before it is set). Any decent compiler with warnings enabled would have caught that.
As a direct answer to your question, you'll just have to go through and spot every system call or library function call that is capable of returning errors, see if you are checking for them, and add checks if they are not already there. fork, malloc, dup2 — everything.

Resources