zoukankan      html  css  js  c++  java
  • 20165223 《信息安全系统设计基础》 实现mybash

    一、了解 mybash

    1. 简介

      bash 是 Bourne Again Shell 的缩写,是linux默认的标准shell(也是大家常说的系统内核),bash也是Unix/Linux上常见的Shell脚本解释器,既然bash是标准的shell,那么就有非标准的sh,csh,ksh等等,我们常说有多少种Shell,其实说的是Shell脚本解释器,Shell是一种脚本语言,那么,就必须有解释器来执行这些脚本,bash是基于Bourne shell创建的,并且吸收了C shell和Korn shell的一些特性,而且bash完全兼容sh,也就是说,用sh写的脚本可以不加修改的在bash中执行。

    ——摘自《Unix/Linux里面的bash是什么

    • 通常shell中执行命令的流程都是bash进程创建了一个子进程,然后子进程进程替换,替换为可执行的命令文件。
    • bash shell是sh shell的增强版本,目前linux大部分(默认)使用的都是bash shell。

    2. mybash 输出信息格式

    • [用户名@主机名所在文件]$(root用户:[用户名@主机名所在文件]#)
    • 当所在文件就是当前用户的home目录时显示“~”

    二、研究 mybash 实现需要的系统调用

    (1)fork()

      fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,每个进程都启动一个从代码的同一位置开始执行的线程,父子两个进程中的线程能同时执行不同的指令要求。
    (1)当调用fork()函数时,在该位置进程一分为二,一个是父进程,一个是子进程。
    (2)若调用成功返回的是两个值:父进程返回的值为子进程标志;子进程返回的值为0,不成功返回为-1。

    • 使用man fork查看

    • 找到需要的头文件和函数参数
    #include <unistd.h>
    
    pid_t fork(void);
    
    

    (2)exec()

      系统调用 execv() 对当前进程进行替换,替换者为一个指定的可执行程序,其参数包括文件名(filename)、参数列表(argv) 以及环境变量 (envp) 。exec函数族不止一个,但它们大致相同,在 Linux 中,它们分别是:execl,execlp,execle,execv,execve 和 execvp 。

    • 使用man exec查看

    • 找到需要的头文件和函数参数
    #include <unistd.h>
    
    int execv(const char *path, char *const argv[]);
    
    

    (3)wait()

      wait() 函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。若该父进程没有子进程或者它的子进程已经结束,wait() 函数就会立即返回。

      waitpid() 的作用和 wait() 一样,但它并不一定要等待第一个终止的子进程(它可以指定需要等待终止的子进程),它还有若干选项,如可提供一个非阻塞版本的 wait() 功能,也能支持作业控制。实际上,wait() 函数只是 waitpid()函数的一个特例,在 Linux 内部实现 wait() 函数时直接调用的就是 waitpid() 函数。

    • 使用man wait查看

    • 找到需要的头文件和函数参数
    #include <sys/types.h>
    #include <sys/wait.h>
    
    pid_t wait(int *status);
    pid_t waitpid(pid_t pid, int *status, int options);
    
    

    三、实现 mybash 的伪代码

    (1)读取用户输入的指令
    (2)调用fork函数生成一个子进程,并将fork返回的pid值赋给父进程fpid
    (3)调用wait函数,传入参数NULL
    (4)判断fpid是否为0
    (5)若为0,则调用execvp函数,将用户输入的指令传进去,实现功能
    (6)若不为0,则提示错误,并返回(1)等待用户下一个指令

    四、实现 mybash

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <sys/types.h>
    #include <sys/utsname.h>
    #include <pwd.h>
    #include <sys/wait.h>
    #define LEN 10
    
    char* Cmd[LEN] = {0};
    int count = 0;
    char OLDPWD[1024] = {0};
    void out_flag()
    {
    	char flag = '$';
    	struct passwd *pw = getpwuid(getuid());
    	if(getuid() == 0)
    	{
    		flag = '#';
    	}
    	struct utsname host;
    	uname(&host);
    	char *hostname = strtok(host.nodename, ".");
    	char path[128] = {0};
    	getcwd(path, 127);//获取当前目录的绝对路径
    	char *p = strtok(path, "/");
    	char *nowdir = NULL;
    	while(p!= NULL)
    	{
    		nowdir = p;
    		p = strtok(NULL, "/");
    	}
    	if(nowdir == NULL)
    	{
    		nowdir = "/";
    	}
    	if(strcmp(nowdir, pw->pw_name) == 0)
    	{
    		nowdir = "~";
    	}
    	printf("[%s@%s %s]mybash%c ", pw->pw_name, hostname, nowdir, flag);
    	fflush(stdout);
    }
    void cut_cmd(char *cmd)
    {
    	char *p = strtok(cmd, " ");
    	while(p != NULL)
    	{
    		Cmd[count++] = p;
    		p = strtok(NULL, " ");
    	}
    }
    int special_cmd()
    {
    	//cd exit 
    	if(strncmp("cd", Cmd[0], 2) == 0)
    	{
    		if(Cmd[1] == NULL || strncmp(Cmd[1], "~", 1) == 0)
    		{
    			//切换到家目录
    			struct passwd *pw = getpwuid(getuid());
    			Cmd[1] = pw->pw_dir;
    		}
    		else if(strncmp(Cmd[1], "-", 1) == 0)
    		{
    			//切换到家目录到上一次所在目录
    			if(strlen(OLDPWD) == 0)
    			{
    				printf("mybash: cd :: OLDPWD not set
    ");
    				return 1;
    			}
    			Cmd[1] = OLDPWD;
    			printf("%s
    ", Cmd[1]);
    		}
    		char str[1024] = {0};
    		getcwd(str, 1023);
    		chdir(Cmd[1]); // 切换路径
    		strcpy(OLDPWD, str);
    		return 1;
    	}
    	if(strncmp("exit", Cmd[0], 4) == 0)
    	{
    		exit(0);
    	}
    	return 0;
    }
    void clear_cmd()
    {
    	int i = 0;
    	for(;i < count; ++i)
    	{
    		Cmd[i] = 0;
    	}
    	count = 0;
    }
    void main()
    {
    	while(1)
    	{
    		out_flag();
    		char cmd[128] = {0};
    		fgets(cmd, 128, stdin); //获取命令
    		cmd[strlen(cmd) - 1] = 0; //去掉最后一个回车符
    		if(strlen(cmd) == 0) // 判别用户的无效输入
    		{
    			continue;
    		}
    		cut_cmd(cmd); // 切割cmd
    		int res = special_cmd(); // 判别是否是需要集成到bash中的特殊命令
    		if(res == 1)
    		{
    			clear_cmd(); //清空全局的指针数组,并将count归0
    			continue;
    		}
    		pid_t pid = fork();
    		assert(pid != -1);
    		if(pid == 0)
    		{
    			// 用命令的可执行文件(./mypwd)替换当前进程
    			char path[1024] = "/home/20165223cn/week9/mypwd/";
    			if(strstr(Cmd[0], "/") != NULL)
    			{
    				memset(path, 0, 1024);
    			}
    			strcat(path, Cmd[0]);
    			execv(path, Cmd);
    			printf("mybash: %s : command not found
    ", Cmd[0]);
    			exit(0);
    		}
    		else
    		{
    			wait(NULL);
    		}
    		clear_cmd();
    	}
    }
    
    

    四、产品截图

    • 测试 mybash

    • 用可执行文件 ./mypwd 替代当前进程执行(实现mypwd)

  • 相关阅读:
    php数组操作
    DedeCMS栏目页调用当前栏目名和上级栏目名
    ThinkPHP递归删除栏目
    WebUploader
    js中的事件委托或是事件代理
    thinkphp框架if标签条件表达式
    sublime插件安装
    thinkphp分页
    织梦自定义变量使用
    php foreach用法和实例
  • 原文地址:https://www.cnblogs.com/moddy13162201/p/10015662.html
Copyright © 2011-2022 走看看