通常shell中执行命令的流程都是bash进程创建了一个子进程,然后子进程进程替换,替换为可执行的命令文件。
1、mybash函数:输出信息的格式是:[用户名@主机名所在文件]$(root用户:[用户名@主机名所在文件]#)。当所在文件就是当前用户的家目录时显示“~”。
那么,该如何得到用户名,主机名和当前所在文件名呢?
我们需要借助下面这些函数:
uid_t geteuid(void)函数取得执行目前进程有效的用户识别码。有效的用户识别码用来决定进程执行的权限,改变此值,进程可以获得额外的权限。
struct passwd *getpwuid(uid_t uid)函数通过用户的uid查找用户的passwd数据。如果出错时,它们都返回一个空指针并设置errno的值,用户可以根据perror函数查看出错的信息。
struct passwd *getpwnam(const char *name)函数获取用户登录相关信息,其中name是当前登录的用户名。若成功,返回指针;若出错或者达到文件尾端,返回NULL。
int uname(struct utsname *name)函数把主机信息写入name参数指向的结构体中。
char *getcwd(char *buf, size_t size)函数把当前目录的绝对地址保存到 buff中,buff 的大小为 size。如果 size太小无法保存该地址,返回 NULL 并设置 errno 为 ERANGE。可以采取令 buff 为 NULL并使 size 为负值来使 getcwd 调用 malloc 动态给 buff 分配,但是这种情况要特别注意使用后释放缓冲以防止内存泄漏。
需注意的是:1.使用函数uname()得到的主机名“.”之后的部分是多余的,需要进行额外的处理。2.getcwd()函数得到的是函数的绝对路径,在显示的信息中只需要当前目录的名字,需要进行处理。
代码实现:
#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函数:su命令切换用户成功后,su进程会新建一个子进程并把它替换为bash进程,su之后的命令都是在新建的bash里运行的。
代码实现
int main(int argc,char *argv[])
{
char *user="root";//指针
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函数:printf(“033[%d;%dH”,0,0)代表清屏且回到原点(0,0)。
代码实现:
#include <stdio.h>
void main()
{
printf("033\[2J");
printf("033[%d;%dH",0,0);
}
注:mysu函数编译时要用gcc -o su su.c -lcrypt