操作系统实验报告

nuaa2023操作系统实验报告

Job6/sh4

题目要求:

重新编写makefile;

实现连接多条命令;

编写测试用例。

解决思路:

重新编写makefile:

根据教学视频所讲述模板进行编写,得到自动生成依赖关系makefile文件。在 Makefile.dep 中定义依赖关系,然后在 Makefile 文件中引用依赖关系进行编 译等操作。

实现连接多条命令:

创建管道,前一个命令的输出作为下一个命令的输入,管道的创建和关闭依靠函数对命令数组进行遍历,将命令的输入输出连接起来,在子进程中,函数执行当前命令,并将输出写入管道;在父进程中,函数等待子进程执行完毕,并将管道的输入端设置为下一个命令的输入。

编写测试用例:

对几种测试用例进行实现。

关键代码:

重写makefile:

OBJS= parse.o cmd.o utest.o main.o

all : $(OBJS)
		cc -o sh4 $(OBJS)
%.o: %.c
		cc -c -o $@ $<
run:
		./sh4 -utest
clean:
		rm -f sh4 *.o
include Makefile.dep

exec_cmd函数实现

void exec_cmd(struct cmd *cmd)
{
	int fd;
	int error;
	if (cmd->input) {
		fd = open(cmd->input, O_RDONLY);
		if (fd < 0) {
			perror("open");
			return ;
		}
		dup2(fd, 0);
		close(fd);
	}
	if (cmd->output) {
		fd = open(cmd->output, O_CREAT | O_WRONLY, 0666);
		if (fd < 0) {
			perror("open");
			return ;
		}
		dup2(fd, 1);
		close(fd);
	}
	error = execvp(cmd->argv[0], cmd->argv);
	if (error < 0 ) 
		perror("execvp");
	return ;
}

exec_pipe_cmd函数

void exec_pipe_cmd(int cmdc, struct cmd *cmdv)
{
	if (cmdc == 1) 
	{	
		exec_cmd(cmdv+0);
		return ;
	}
	pid_t pid;
	int fds[2];
	pipe(fds);
	pid = fork();
	if (pid == -1) {
		perror("fork");
		return ;
	}
	if (pid == 0) {
		dup2(fds[1], 1);
		close(fds[1]);
		close(fds[0]);
		exec_pipe_cmd(cmdc-1 ,cmdv);
		exit(0);
	}
	wait(NULL);
	dup2(fds[0], 0);
	close(fds[1]);
	close(fds[0]);
	exec_cmd(&cmdv[cmdc-1]);
	
}
// cat <test.in | sort | uniq | cat >test.out
void test_exec_pipe_cmd()
{
    unlink("test.out");

pid_t pid = fork();
if (pid == 0) { 
    // echo "3\n1\n2\n3\n2" | cat >log.txt
    struct cmd cmdv[4] = {{1, {"cat", NULL}, "test.in", NULL},
						  {1, {"sort", NULL}, NULL, NULL},
						  {1, {"uniq", NULL}, NULL, NULL},
						  {1, {"cat", NULL}, NULL, "test.out"}};
	exec_pipe_cmd(4, cmdv);
}
wait(NULL);

read_and_cmp("test.out", "1\n2\n3\n");

}
void parse_cmd(char *line, struct cmd *cmd)
{
	char* word;
	cmd->argc = 0;
	cmd->input = NULL;
	cmd ->output = NULL;
	word = strtok(line, " ");
	while (word) {
		if (word[0] == '>')	{
			if (strlen(word) == 1) {
				word = strtok(NULL, " ");
				cmd->output = word;
			}
			else 
				cmd->output = word+1;
		}
		else if (word[0] == '<') {
			if( strlen(word) == 1) {
				word = strtok(NULL, " ");
			   	cmd->input =word;	
			}
			else 
				cmd->input = word+1;
		}
		else {
			cmd->argv[cmd->argc] = word;
			cmd->argc++;
		}
		word = strtok(NULL, " ");
	}
	cmd->argv[cmd->argc] = NULL;
}

void dump_cmd(struct cmd *cmd)
{	
	printf("\nargc = %d\n", cmd->argc);
	int i;
	for (i = 0; i < cmd->argc; i++) {
		printf("argv[%d] = (%s)\n", i, cmd->argv[i]);
	}
	if (cmd->input)
		printf("input = (%s)\n", cmd->input);
	else
		printf("input = null\n");
	if (cmd->output)
		printf("output = (%s)\n", cmd->output);
	else
		printf("output = null\n");
}
int parse_pipe_cmd(char *line, struct cmd *cmdv)
{
    int cmdc = 0;
	char* save;
	char* part;
	part = strtok_r(line, "|", &save);
	while (part) {
		parse_cmd(part, cmdv+cmdc);
		cmdc++;
		part = strtok_r(NULL, "|", &save);
	}
	//dump_pipe_cmd(cmdc, cmdv);
	return cmdc;
}

void dump_pipe_cmd(int cmdc, struct cmd *cmdv)
{
    int i;

printf("pipe cmd, cmdc = %d\n", cmdc);
for (i = 0; i < cmdc; i++) {
    struct cmd *cmd = cmdv + i;
    dump_cmd(cmd);
}

}
// echo abc
void test_parse_cmd_1()
{
    struct cmd cmd;
    char line[] = "echo abc xyz";
    parse_cmd(line, &cmd);

assert(cmd.argc == 3);
assert(strcmp(cmd.argv[0], "echo") == 0);
assert(strcmp(cmd.argv[1], "abc") == 0);
assert(strcmp(cmd.argv[2], "xyz") == 0);
assert(cmd.argv[3] == NULL);

}

// echo abc >log
void test_parse_cmd_2()
{	
	struct cmd cmd;
	char line[] = "echo abc >log";
	parse_cmd(line, &cmd);
	

assert(cmd.argc == 2);

assert(strcmp(cmd.argv[0], "echo") == 0);
assert(strcmp(cmd.argv[1], "abc") == 0);
assert(cmd.argv[2] == NULL);
assert(strcmp(cmd.output, "log") == 0);

}

// cat /etc/passwd | wc -l
void test_parse_pipe_cmd_1()
{
	struct cmd cmdv[MAX_TEST_CMDV];
	char line[] = "cat /etc/passwd | wc -l";
	int cmdc = 0;
	cmdc = parse_pipe_cmd(line, cmdv);
	

assert(cmdc == 2);

assert(cmdv[0].argc == 2);
assert(strcmp(cmdv[0].argv[0], "cat") == 0);
assert(strcmp(cmdv[0].argv[1], "/etc/passwd") == 0);
assert(cmdv[0].argv[2] == NULL);

assert(cmdv[1].argc == 2);
assert(strcmp(cmdv[1].argv[0], "wc") == 0);
assert(strcmp(cmdv[1].argv[1], "-l") == 0);
assert(cmdv[1].argv[2] == NULL);

}

// cat <input | sort | cat >output
void test_parse_pipe_cmd_2()
{
	struct cmd cmdv[MAX_TEST_CMDV];
	char line[] = "cat <input | sort | cat >output";
	int cmdc = 0;
	cmdc = parse_pipe_cmd(line, cmdv);

assert(cmdc == 3);

assert(cmdv[0].argc == 1);
assert(strcmp(cmdv[0].argv[0], "cat") == 0);
assert(cmdv[0].argv[1] == NULL);
assert(strcmp(cmdv[0].input, "input") == 0);

assert(cmdv[1].argc == 1);
assert(strcmp(cmdv[1].argv[0], "sort") == 0);
assert(cmdv[1].argv[1] == NULL);

assert(cmdv[2].argc == 1);
assert(strcmp(cmdv[2].argv[0], "cat") == 0);
assert(cmdv[2].argv[1] == NULL);
assert(strcmp(cmdv[2].output, "output") == 0);

}

运行结果:

test_parse_cmd_1 pass
test_parse_cmd_2 pass
test_parse_pipe_cmd_1 pass
test_parse_pipe_cmd_1 pass
test_exec_cmd pass
test_exec_pipe_cmd pass
All test have passed!
>pwd
current working directory:/root/os/job6/sh4
>ls
Makefile Makefile.dep cmd.c cmd.h cmd.o main.c main.o parse.c
parse.h parse.o readme.md sh4 test test.in test.out
>exit

Job8/pc1.c

题目要求:

  • 系统中有3个线程:生产者、计算者、消费者
  • 系统中有2个容量为4的缓冲区:buffer1、buffer2
  • 生产者
    • 生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符
    • 放入到buffer1
    • 打印生产的字符
  • 计算者
    • 从buffer1取出字符
    • 将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印
    • 放入到buffer2
  • 消费者
    • 从buffer2取出字符
    • 打印取出的字符
  • 程序输出结果(实际输出结果是交织的)
    a
    b
    c

    a:A
    b:B
    c:C

    A
    B
    C

解决思路:

定义一个buffer结构体,用来存储buffer1与buffer2状态,定义buffer_is_empty(struct array a)buffer_is_full(struct array a)get_item(struct array *a)put_item(struct array *a, char item)函数。

总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者。

1.生产者产生字符后,查看buffer1的锁状态,加锁,之后判断buffer1是否为满,满则阻塞等待,反之放入缓冲区buffer1,之后唤醒等待的计算者线程,解开锁。

2.计算者被唤醒后,获取buffer1的锁,加锁,判断buffer1是否为空,如果空则阻塞等待,反之取出一个字符,转化为大写形式。之后再唤醒生产者,释放锁。接下来,它作为消费者的生产者,查看buffer2的锁状态,加锁,之后判断buffer2是否为满,满则阻塞等待,反之将字符放入缓冲区buffer2,之后唤醒等待的消费者线程,解开锁。

3.消费者被唤醒后,获取buffer2的锁,加锁,判断buffer2是否为空,如果空则阻塞等待,反之取出一个字符,输出到屏幕上,之后唤醒消费者,解开锁。

关键代码:

消费者:

void *consume(void *arg) {
	char item;
	int i; 
	for (i = 0; i < ITEM_COUNT; i++) {
		pthread_mutex_lock(&mutex);
		while (buffer_is_empty(buffer2))
			pthread_cond_wait(&wait_full_buffer2, &mutex);

	item = get_item(&buffer2);
	printf("    consume item: %c\n", item);

	pthread_cond_signal(&wait_empty_buffer2);
	pthread_mutex_unlock(&mutex);

}

return NULL;

}

计算者:

void *calculator(void *arg) {
	char item;
	int i;
	for (i = 0; i < ITEM_COUNT; i++) {
		pthread_mutex_lock(&mutex);
		while (buffer_is_empty(buffer1))
			pthread_cond_wait(&wait_full_buffer1, &mutex);
		while (buffer_is_full(buffer2))
			pthread_cond_wait(&wait_empty_buffer2, &mutex);
		

	item = get_item(&buffer1);
	printf("%c:%c\n", item, item - 32);
	item -= 32;
	put_item(&buffer2,item);

	pthread_cond_signal(&wait_full_buffer2);
	pthread_cond_signal(&wait_empty_buffer1);
	pthread_mutex_unlock(&mutex);

}

return NULL;

}

生产者:

void *produce() {
	char item;
	int i;
	for (i = 0; i < ITEM_COUNT; i++) {
		pthread_mutex_lock(&mutex);
		while (buffer_is_full(buffer1))
			pthread_cond_wait(&wait_empty_buffer1, &mutex);

	item = i + 'a';
	put_item(&buffer1, item);
	printf("produce item: %c\n", item);

	pthread_cond_signal(&wait_full_buffer1);
	pthread_mutex_unlock(&mutex);

}

return NULL;

}

主函数:

int main() {
	

pthread_t consumer_tid, calculator_tid;

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&wait_empty_buffer1, NULL);
pthread_cond_init(&wait_empty_buffer2, NULL);
pthread_cond_init(&wait_full_buffer1, NULL);
pthread_cond_init(&wait_full_buffer2, NULL);

pthread_create(&calculator_tid, NULL, calculator, NULL);
pthread_create(&consumer_tid, NULL, consume, NULL);
produce();

pthread_join(consumer_tid, NULL);
pthread_join(calculator_tid, NULL);

return 0;

}

运行结果:

produce item: a
produce item: b
a:A
produce item: c
    consume item: A
b:B
produce item: d
    consume item: B
c:C
produce item: e
    consume item: C
d:D
produce item: f
    consume item: D
e:E
produce item: g
    consume item: E
f:F
produce item: h
    consume item: F
g:G
    consume item: G
h:H
    consume item: H

Job8/pc2.c

题目要求:

使用信号量解决生产者、计算者、消费者问题
功能与 job8/pc1.c相同

解决思路:

引入信号量结构体,并且定义多个信号量,通过sema_init()对信号量进行初始化,sema_wait()进行等待,sema_signal()进行释放。

总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者,按一定的逻辑执行PV操作。

生产者首先执行 sema_wait(&empty_buffer1_sema)函数,如果buffer1存在空闲位置, empty_buffer1_sema减1,对buffer1进行加锁,加入字符后进行解锁。之后执行sema_signal(&full_buffer1_sema);进行加1操作后,唤起计算者进程。

唤起计算者进程后,首先执行sema_wait(&full_buffer1_sema);函数,如果buffer1存在值,则full_buffer1_sema减1,加buffer1的锁,取字符转换为大写形式,解开锁,之后执行sema_signal(&empty_buffer1_sema);此时empty_buffer1_sema加1,唤醒等待的生产者线程。之后作为生产者,向buffer2写字符,首先执行sema_wait(&full_buffer2_sema) 函数,判断是否buffer2有值,如果等到有值的情况,则full_buffer2_sema减1,加buffer1的锁,取字符转换为大写形式,解开锁,再执行ema_signal(&empty_buffer2_sema),此时empty_buffer2_sema加1,唤醒等待的消费者线程。

消费者首先执行sema_wait(&full_buffer2_sema);buffer2进行判断,如果有值,则full_buffer2_sema减1,加buffer2的锁,取字符打印输出,解开锁,再执行sema_signal(&empty_buffer2_sema),此时empty__buffer2_sema加1,唤醒等待的计算者线程。

关键代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define CAPACITY 4
#define ITEM_COUNT 8
int buffer1[CAPACITY], buffer2[CAPACITY];
int in1, in2;
int out1, out2;

typedef struct {
  int value;
  pthread_mutex_t mutex;
  pthread_cond_t cond;
} sema_t;
sema_t mutex1_sema, mutex2_sema;
sema_t empty_buffer1_sema, empty_buffer2_sema;
sema_t full_buffer1_sema, full_buffer2_sema;


// buffer1
int buffer1_is_empty() {
  return in1 == out1;
}

int buffer1_is_full() {
  return (in1 + 1) % CAPACITY == out1;
}

int get_item1() {
  int item = buffer1[out1];
  out1 = (out1 + 1) % CAPACITY;
  return item;
}

void put_item1(int item) {
  buffer1[in1] = item;
  in1 = (in1 + 1) % CAPACITY;
}

// buffer2
int buffer2_is_empty() {
  return in2 == out2;
}

int buffer2_is_full() {
  return (in2 + 1) % CAPACITY == out2;
}

int get_item2() {
  int item = buffer2[out2];
  out2 = (out2 + 1) % CAPACITY;
  return item;
}

void put_item2(int item) {
  buffer2[in2] = item;
  in2 = (in2 + 1) % CAPACITY;
}


// sema
void sema_init(sema_t *sema, int value) {
  sema->value = value;
  pthread_mutex_init(&sema->mutex, NULL);
  pthread_cond_init(&sema->cond, NULL);
}

void sema_wait(sema_t *sema) {
  pthread_mutex_lock(&sema->mutex);
  while(sema->value <= 0)
    pthread_cond_wait(&sema->cond, &sema->mutex);
  --sema->value;
  pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(sema_t *sema) {
  pthread_mutex_lock(&sema->mutex);
  ++sema->value;
  pthread_cond_signal(&sema->cond);
  pthread_mutex_unlock(&sema->mutex);
}


// 生产者
void *produce(void *arg) {
  int item, i;

  for(i = 0; i < ITEM_COUNT; i++) {
    sema_wait(&empty_buffer1_sema);
    sema_wait(&mutex1_sema);

item = 'a' + i;
printf("produce item: %c\n", item);
put_item1(item);

sema_signal(&mutex1_sema);
sema_signal(&full_buffer1_sema);

  }
  return NULL;
}

// 计算者
void *compute(void *arg) {
  int item, i;

  for(i = 0; i < ITEM_COUNT; i++) {
    sema_wait(&full_buffer1_sema);
    sema_wait(&mutex1_sema);
    

item = get_item1();
printf("\t\tcompute item before: %c\n", item);
item = item - 32;
printf("\t\tcompute item after : %c\n", item);

sema_signal(&mutex1_sema);
sema_signal(&empty_buffer1_sema);

sema_wait(&empty_buffer2_sema);
sema_wait(&mutex2_sema);

put_item2(item);

sema_signal(&mutex2_sema);
sema_signal(&full_buffer2_sema);

  }
  return NULL;
}

// 消费者
void *consume(void *arg) {
  int item, i;

  for(i = 0; i < ITEM_COUNT; i++) {
    sema_wait(&full_buffer2_sema);
    sema_wait(&mutex2_sema);
    

item = get_item2();
printf("\t\t\t\t\tconsume item: %c\n", item);

sema_signal(&mutex2_sema);
sema_signal(&empty_buffer2_sema);

  }
  return NULL;
}


int main() {
  pthread_t computer_tid, consumer_tid;

  sema_init(&mutex1_sema, 1);
  sema_init(&mutex2_sema, 1);
  sema_init(&empty_buffer1_sema, CAPACITY - 1);
  sema_init(&empty_buffer2_sema, CAPACITY - 1);
  sema_init(&full_buffer1_sema, 0);
  sema_init(&full_buffer2_sema, 0);

  pthread_create(&computer_tid, NULL, compute, NULL);
  pthread_create(&consumer_tid, NULL, consume, NULL);
  produce(NULL);
  pthread_join(computer_tid, NULL);
  pthread_join(consumer_tid, NULL);

  return 0;
}

运行结果:

produce item: a
produce item: b
produce item: c
                compute item before: a
                compute item after : A
produce item: d
                                        consume item: A
                compute item before: b
                compute item after : B
produce item: e
                                        consume item: B
                compute item before: c
                compute item after : C
produce item: f
                                        consume item: C
                compute item before: d
                compute item after : D
produce item: g
                                        consume item: D
                compute item before: e
                compute item after : E
produce item: h
                                        consume item: E
                compute item before: f
                compute item after : F
                compute item before: g
                compute item after : G
                                        consume item: F
                compute item before: h
                compute item after : H
                                        consume item: G
                                        consume item: H

Job9/pfind.c

题目要求:

功能与 sfind 相同

要求使用多线程完成

主线程创建若干个子线程

主线程负责遍历目录中的文件

遍历到目录中的叶子节点时

将叶子节点发送给子线程进行处理

两者之间使用生产者消费者模型通信

主线程生成数据

子线程读取数据

解决思路:

分不同情况,如果路径为文件,则对文件进行查找是否有包含指定的字符串,输出行。如果路径为目录,则遍历目录与其子目录,将得到的文件路径与搜索的字符串添加到任务队列中。

创建WORKER_NUMBER个子进程,不同子进程从任务队列中获取任务并执行,即对指定文件进行查找。当任务队列为空时,等待新的任务加入。

主线程完成遍历后,根据WORKER_NUMBER添加标记,表示所有任务完成,子线程得到标记后,自动退出线程。

关键代码:

void find_file(char *path,char *target)
{
	FILE *file = fopen(path,"r");

char line[256];
while (fgets(line,sizeof(line),file)){
	if(strstr(line,target))
		printf("%s: %s",path,line);
}

fclose(file);

}

void *worker_entry(void *arg)
{
	while(1){
		struct task task;
		task=get_item();
		if(task.is_end)
		{	
			struct task tm;
			tm.is_end=1;
			put_item(tm);
			break;
		}
		find_file(task.path,task.string);
	}
}

void find_dir(char *path,char *target)
{
	DIR *dir = opendir(path);
	struct dirent *entry;
	struct task buffer;
	while(entry = readdir(dir)){
		if(strcmp(entry->d_name,".") == 0)
			continue;
		if(strcmp(entry->d_name,"..") == 0)
			continue;
		if(entry->d_type == DT_DIR)
		{
			char base[80];
			memset(base,'\0',sizeof(base));
			strcpy(base,path);
			strcat(base,"/");	
			strcat(base,entry->d_name);
			find_dir(base,target);		
			

	}
	if(entry->d_type == DT_REG)
	{
		char base[80];
		memset(base,'\0',sizeof(base));
		strcpy(base,path);
		strcat(base,"/");
		strcat(base,entry->d_name);
			buffer.is_end=0;
			strcpy(buffer.path,base);
			strcpy(buffer.string,target);
			put_item(buffer);
			break;
		}
	}
	closedir(dir);

}

main函数

int main(int argc,char *argv[])
{
	char *path = argv[1];
	char *string = argv[2];

if(isfile(path))
{
	find_file(path,string);
	return 0;
}
pthread_t consumer_tid[WORKER_NUMBER];
	find_dir(path,string);
	struct task buf;
	buf.is_end=1;
	put_item(buf);
for(int i=0;i<WORKER_NUMBER;i++)
{
		pthread_create(&consumer_tid[i],NULL,worker_entry,NULL);
}
for(int i=0;i<WORKER_NUMBER;i++)
{
	pthread_join(consumer_tid[i],NULL);
}

return 0;

运行结果:

root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# cc pfind.c -o pfind -lpthread
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test main
test/hello/hello.c: int main() {
test/world/world.c: int main()
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test int
test/hello/hello.c: int main() {
test/hello/hello.c: 	printf("hello");
test/world/world.c: int main()
test/world/world.c: 	printf("world");

Job11/http服务器

题目要求:

完成多进程版本的 httpd

放置在 job10/http/concurrent 目录下

解决思路:

​ 主函数首先解析命令行参数,获取要监听的 IP 地址和端口号。然后使用 tcp_listen 函数创 建一个 TCP 服务器,该服务器会监听指定的 IP 地址和端口号。 在服务器开始循环等待客户端连接时,使用 tcp_accept 函数接受一个新的连接,然后使用 fork 函数创建一个子进程来处理客户端连接。在子进程中,调用 http_handler 函数来处 理客户端的 HTTP 请求,并在处理完毕后关闭连接。

​ 通过 http_parse_req 函数解析客户端发送的请求,获取请求的路径。

​ 判断路径是否以 “/app/” 开头,如果是则认为客户端请求的是一个 CGI 脚本,需要执行该脚本 并将输出发送给客户端。

​ 如果路径不是以 “/app/” 开头,则判断路径对应的是一个文件还是目录,如果是目录则调用 generate_directory_listing 函数生成一个 HTML 目录列表并发送给客户端,如果是文 件则直接将文件内容发送给客户端。

​ 在向客户端发送响应时,使用分块传输编码(Chunked Transfer Encoding)进行传输,这是 一种将消息分块传输的方式,可以在发送大量数据时提高传输效率。

关键代码

http.c

#include "std.h"
#include "http.h"
#include "errno.h"

char *web_root = "www";
void http_write_chunk(FILE *fw, void *chunk, int size)
{
    fprintf(fw, "%x\r\n", size);
    fwrite(chunk, size, 1, fw);
    fprintf(fw, "\r\n");
}
void http_end(FILE *fw)
{
    http_write_chunk(fw, NULL, 0);
    fflush(fw);
}

void http_prints(FILE *fw, void *string)
{
    int size = strlen(string);
    http_write_chunk(fw, string, size);
}

void http_printf(FILE *fw, const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    char chunk[100];
    int size = vsprintf(chunk, format, ap);
    va_end(ap);

http_write_chunk(fw, chunk, size);

}

void http_send_headers(FILE *fp, char *content_type)
{
    fprintf(fp, "HTTP/1.1 200 OK\r\n");
    fprintf(fp, "Server: tiny httpd\r\n");
    fprintf(fp, "Content-type: %s\r\n", content_type);
    fprintf(fp, "Transfer-Encoding: chunked\r\n");
    fprintf(fp, "\r\n");
}

// GET /index.html HTTP/1.1\r\n
char *http_parse_req(FILE *fr, char *req, int req_size)
{
    fgets(req, req_size, fr);
    char *method = strtok(req, " ");
    char *path = strtok(NULL, " ");
    char *protocol = strtok(NULL, "\r");
    if (strcmp(path, "/") == 0) // if path is "/" replace it with "/index.html"
    {
        path = "/index.html";
    }
    static char http_path[1024];
    sprintf(http_path, "%s%s", web_root, path); // prepend "www" to the path
    return http_path;
}
char *generate_directory_listing(const char *real_path, const char *req_path)
{
    DIR *d;
    struct dirent *dir;
    d = opendir(real_path);
    if (d == NULL) {
        return NULL;
    }

size_t html_size = 4096;  // Increase initial buffer size.
char *html = malloc(html_size);
if (html == NULL) {
    closedir(d);
    return NULL;
}

size_t length = sprintf(html, "<html><body><h1>Index of %s</h1><hr>", req_path);

// Get parent directory path
char *parent_dir = strdup(req_path);
char *last_slash = strrchr(parent_dir, '/');
if (last_slash != NULL && last_slash != parent_dir) {
    *last_slash = '\0';  // Remove the last directory from the path
    length += sprintf(html + length, "<a href='%s'>Parent dir</a><br>", parent_dir);  // Add parent directory link
} else {
    length += sprintf(html + length, "<a href='/'>Parent dir</a><br>");  // Add parent directory link to the root
}
free(parent_dir);

while ((dir = readdir(d)) != NULL) {
    size_t new_length = length + strlen(dir->d_name) * 2 + strlen(req_path) + 27;  // Calculate new length after adding the directory name and request path.
    if (new_length >= html_size) {  // If buffer is not large enough, increase its size.
        html_size *= 2;
        char *new_html = realloc(html, html_size);
        if (new_html == NULL) {  // If realloc failed, clean up and return.
            free(html);
            closedir(d);
            return NULL;
        }
        html = new_html;
    }
    length += sprintf(html + length, "<a href='%s/%s'>%s</a><br>", req_path, dir->d_name, dir->d_name);  // Append directory name with request path as prefix.
}

sprintf(html + length, "<hr></body></html>");  // Append closing tags.
closedir(d);
return html;

}

void http_handler(int fd)
{
    FILE *fr = fdopen(fd, "r");
    FILE *fw = fdopen(fd, "w");

char req[1024];
char *http_path;
http_path = http_parse_req(fr, req, sizeof(req));
fprintf(stderr, "http path: %s\n", http_path); 
char *app_prefix = "www/app";
if (strncmp(http_path, app_prefix, strlen(app_prefix)) == 0) {
    // Get the script name by removing the "/app/" prefix
    char *script_name = http_path;

   if(strstr(script_name,"show_env")!=NULL)
    {
        script_name="docs/exec";
    }
    else if(strstr(script_name,"add_student?")!=NULL || strstr(script_name,"remove_student?")!=NULL)
    {
        char *cmd;
        if(strstr(script_name,"add_student?")!=NULL)
        {
            cmd="./www/app/add_student";
        }
        else{
            cmd="./www/app/remove_student";
        }
        // Get the query by removing the "/add_student?" prefix
        char *query = script_name + strlen(cmd)-1;
        char envv_query[1024];
        sprintf(envv_query, "QUERY_STRING=%s", query);
        fprintf(stderr, "envv_query: %s\n", envv_query); 
        // Create a pipe
        int pipefd[2];
        if (pipe(pipefd) == -1) {
            perror("pipe");
            exit(EXIT_FAILURE);
        }

        // Fork a new process
        int pid = fork();
        if (pid == 0) {
            // Child process
            close(pipefd[0]);  // Close unused read end
            dup2(pipefd[1], STDOUT_FILENO);  // Redirect stdout to the pipe

            const char *argv[] = {cmd,  NULL};
            const char *envv[] = {envv_query, NULL};
            execve(argv[0], (char *const *)argv, (char *const *)envv);
            perror("execve");
            exit(1); // If execve returns, it must have failed.
        } else if (pid < 0) {
            // Failed to fork
            fprintf(stderr, "Error forking process: %s\n", strerror(errno));
            return;
        } else {
            // Parent process
            close(pipefd[1]);  // Close unused write end

            // Read from the pipe and write to the HTTP response
            http_send_headers(fw, "text/html");
            char buffer[1024];
            size_t bytes;
            while ((bytes = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
                http_write_chunk(fw, buffer, bytes);
            }

            int status;
            waitpid(pid, &status, 0);

            // Check if child process exited normally
            if (WIFEXITED(status)) {
                fprintf(stderr, "Script exit code: %d\n", WEXITSTATUS(status));
            } else {
                fprintf(stderr, "Script did not exit normally\n");
            }
            http_end(fw);
            return;
        }
    }

    fprintf(stderr, "script_name: %s\n", script_name);
    // Form the actual script path
    char script_path[1024];
    sprintf(script_path, "./%s", script_name); // add the './' before the script path
    fprintf(stderr, "Script path: %s\n", script_path);  // Log the actual script path

    // Open a pipe to execute the script and get the output
    FILE *pipe = popen(script_path, "r");
    if (pipe == NULL) {
        fprintf(stderr, "Error executing script: %s\n", strerror(errno));  // Log any error that occurs
        fprintf(fw, "HTTP/1.1 500 Internal Server Error\r\n\r\n");
        return;
    }
    http_send_headers(fw, "text/html");
    char buffer[1024];
    size_t bytes;
    while ((bytes = fread(buffer, 1, sizeof(buffer), pipe)) > 0) {
        http_write_chunk(fw, buffer, bytes);
    }
    int ret = pclose(pipe);
    fprintf(stderr, "Script exit code: %d\n", ret);  // Log the script's exit code
}
else {
    struct stat path_stat;
    stat(http_path, &path_stat);
    if (S_ISDIR(path_stat.st_mode)) { // If the path is a directory
        char *html = generate_directory_listing(http_path, req + 4); // Pass the original request path without "www" prefix.
        if (html == NULL) {
            fprintf(fw, "HTTP/1.1 404 Not Found\r\n\r\n");
            return;
        }
        http_send_headers(fw, "text/html");
        http_prints(fw, html);
        free(html);
    } else { // If the path is a file
        FILE *file = fopen(http_path, "r");
        if (file == NULL) {
            fprintf(fw, "HTTP/1.1 404 Not Found\r\n\r\n");
            return;
        }
        http_send_headers(fw, "text/html");
        char buffer[1024];
        size_t bytes;
        while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) {
            http_write_chunk(fw, buffer, bytes);
        }
        fclose(file);
    }
}

http_end(fw);

}

main.c

#include "std.h"
#include "tcp.h"
#include "http.h"
#include "signal.h"
#include "sys/wait.h"
void usage()
{
    puts("Usage: httpd -p port -h");
    puts("  -p port");
}
void sigchld_handler(int s)
{
    while(waitpid(-1, NULL, WNOHANG) > 0);
}
int main(int argc, char *argv[])
{
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
    perror("sigaction");
    exit(1);
}
char *ip_address = "0.0.0.0";
int port = 80;
int i;
for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-p") == 0) {
        i++;
        port = atoi(argv[i]);
        continue;
    }
}
int server_fd = tcp_listen(ip_address, port);
while (1) {
    int client_fd = tcp_accept(server_fd);
    int pid = fork();
    if (pid < 0) {
        perror("fork");
        close(client_fd);
    } else if (pid == 0) {
        http_handler(client_fd);
        close(client_fd);
        exit(0);
    } else {
        close(client_fd);
    }
}
return 0;

}

运行结果:

http://localhost:8080/ 
Hello World!
/a.html
/b.html
/c.html
/source
/app/now
/app/show_env?name=tom&age=10
/app/list_student
/add_student.html
/remove_student.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__狐阿桔

thanks

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值