Simulated shell terminals in Linux environment: mybash, mysu, myclear

Keywords: shell

Usually the process of executing commands in shell is that the bash process creates a subprocess, and then the subprocess process process is replaced by the executable command file.
1. mybash function: the format of output information is: [user name @ host name in the file]$(root user: [user name @ host name in the file]. Display "~" when the file is the current user's home directory.

So, how to get the user name, host name and current file name?
We need to use the following functions:
The uid_t geteuid(void) function obtains the user identification code that is valid for executing the current process. A valid user identification code is used to determine the permission of the process to execute. By changing this value, the process can gain additional permissions.
The struct passwd *getpwuid(uid_t uid) function finds the user's passwd data through the user's uid. If something goes wrong, they all return a null pointer and set the value of errno. Users can view the error information according to the perror function.
The struct passwd *getpwnam(const char *name) function obtains information about user login, where name is the current login user name. If it succeeds, return the pointer; if it fails or reaches the end of the file, return NULL.
The int uname(struct utsname *name) function writes host information into the structure pointed by the name parameter.
The char *getcwd(char *buf, size_t size) function stores the absolute address of the current directory in buff, which is size. If size is too small to save the address, return NULL and set errno to ERANGE. You can make getcwd call malloc to allocate buff dynamically by making buff NULL and size negative, but in this case special attention should be paid to releasing buffers after use to prevent memory leaks.
It should be noted that: 1. Using the function uname() to get the host name "." after the part is redundant, need additional processing. 2. The getcwd () function obtains the absolute path of the function. In the information displayed, only the name of the current directory needs to be processed.
Code implementation:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <signal.h>

#define LENTH 20
char *cmdArr[LENTH]={NULL};
int count = 0;

void out_flag()
{
    char path[128] = {0};
    if(NULL == getcwd(path, 128))
    {
        printf("getcwd error\n");
        return;
    }

    char dir[128] = {0};
    char *p = strtok(path, "/");
    while(p != NULL)
    {
        strcpy(dir, p);
        p = strtok(NULL, "/");
    }
    if(strlen(dir) == 0)
    {
        strcpy(dir, "/");
    }
    struct passwd *pw = getpwuid(getuid());
    if(pw == NULL)
    {
        printf("error\n");
        return;
    }
    if(strcmp(pw->pw_dir, getcwd(path, 128)) == 0)
    {
        memset(dir, 0, 128);
        strcpy(dir, "~");
    }
    struct utsname host;
    uname(&host);
    char *hname = strtok(host.nodename, ".");
    if(hname == NULL)
    {
        printf("error\n");
        return;
    }
    char flag = '$';
    if(pw->pw_uid == 0)
    {
        flag = '#';
    }
    printf("[%s@%s %s]%c ", pw->pw_name, hname, dir, flag);
    fflush(stdout);
}

2. mysu function: after the Su command switching user succeeds, the Su process will create a new subprocess and replace it with the bash process. The commands after su run in the new bash.
code implementation

int main(int argc,char *argv[])
{
    char *user="root";//Pointer
    if(argv[1]!=NULL)
    {
        user=argv[1];
        struct passwd *p=NULL;
        p=getpwnam(user);
        if(p==NULL)
        {
            printf("not find username!\n");
            return;
        }
    }
    struct passwd *pw=getpwuid(getuid());
    if(strcmp(pw->pw_name,"root")!=0)  
    {
        char passwd[128]={0};
        printf("Passwd:");
        struct termios old,new;
        tcgetattr(0,&old);
        new=old;
        new.c_lflag &= ~ECHO;   
        new.c_lflag &= ~ICANON; 
        tcsetattr(0,TCSANOW,&new);
        char c=0;
        int num=0;
        while((c=getchar())!='\n')
        {
            if(c==127)
            {
                if(num>0)
                {
                    passwd[--num]=0;
                    printf("\033[1D");
                    printf("\033[K");
                }
                continue;
            }
            printf("*");
            fflush(stdout);
            passwd[num++]=c;
        }
        tcsetattr(0,TCSANOW,&old);

        printf("\n");
        struct spwd *sp=getspnam(user);
        assert(sp!=NULL);
        char salt[128]={0}; 
        int i=0;
        int count =0;
        for(;sp->sp_pwdp[i]!=0;i++)
        {
             salt[i]=sp->sp_pwdp[i];
             if(salt[i]=='$')
             {
                 count++;             
                 if(count==3)
                 break;
             }
        }
        char* p=crypt(passwd,salt);
        assert(p!=NULL);

        if(strcmp(p,sp->sp_pwdp)!=0)
        {
            printf("passwd error\n");
            exit(0);
        }
    }
    pid_t pid=fork();
    assert(pid!=-1);
    if(pid==0)
    {
        struct passwd *pw=getpwnam(user);
        assert(pw!=NULL);
        setuid(pw->pw_uid);
        setenv("HOME",pw->pw_dir,1);
        execl(pw->pw_shell,pw->pw_shell,(char *)0);
        printf("error\n");
        exit(0);
    }

    else
    {
        wait(NULL);
    }
    return 0;
}

3.myclear function: printf("033[%d;%dH", 0,0) represents the clearing screen and returns to the origin (0,0).
Code implementation:

#include <stdio.h>

void main()
{
    printf("033\[2J");
    printf("033[%d;%dH",0,0);
}

Note: When compiling mysu function, gcc-o su.c-lcrypt should be used.

Posted by rockintyler on Tue, 18 Dec 2018 02:39:04 -0800