不認真分析問題,後果很嚴重!(Auto的教訓)

==========================
自動化程序管理器 【Auto】
==========================

需求:
日常常常寫一些程序,有些程序雖然不完善,可是從此卻有可能會用到。那麼,你在一個月後甚至幾個月後,還能想起本身以前寫的那個程序的名字和用途麼?恐怕挺難。你如何把本身從此有可能會用到的程序和那些Toy Program分開?創建特殊目錄?那麼,如何在升級程序時,只須要從新編譯而不須要作一些目錄拷貝和同步的工做呢?
凡此種種,我我的以爲很麻煩。另外,有時候,你想對本身以前寫的程序進行一個回顧,或者進行測試。那麼你將會在尋找程序上花一些時間,在會想起這個程序是幹什麼的上,又要話一些時間。真是麻煩。
基於以上幾點,我想本身寫個程序管理器,專門管理本身「有用「的程序。

目標:
自動化程序管理器(如下簡稱Auto)必須能提供如下功能。
1. 註冊程序。把你想要追蹤的程序註冊到Auto上。
2. 註銷程序。若是你不想繼續跟蹤該程序了,移除。
3. 列出已註冊程序列表和描述。
4. 執行已註冊的程序。

指望的用法:(編號和目標編號對應)
1. Auto -r prog1 -m    description    --    register prog1
2. Auto -d prog1                --    deregister prog1
3. Auto -l                        --    list all registered programs
4. Auto -e prog1 args            --    execute prog1 with arguments args
(一次只能作一個動做,好比auto -l -e prog1 args就是錯的!)

概要設計:
client (Auto) <==[socket IPC]==> Server (Autod) <--(read,write)--> config file
client (Auto)充當和用戶的接口,它對用戶的指令進行檢查,若是合法,則把必要信息傳給Server (Autod),請求服務,而且接受Audod的返回。
Server (Audod)充當真正的執行者,提供註冊,註銷,列表,執行等服務。它和config file進行交互。
config file用來存儲目前註冊上的服務的信息。name - path - description.

詳細設計 -- Auto:
Auto的用法和普通Linux command的用法要一致。如指望的用法中所述。
step1: Auto首先檢查命令行參數的合法性。
            =》若是合法,好比Auto -r prog1 -m "xxx",則轉到step2.
            =》若是不合法,好比Auto -r prog1(只註冊而不加描述的行爲在這裏不合法!若是description太長,也不合法!),那麼就給出提示,而且終止。
step2: 創建和Autod的socket鏈接,將必要的信息[act,argc,argv1,arg2,...argvn]經過socket傳輸傳給Autod。
            =》 這裏採用屢次傳輸的方式,這樣比較簡單。
            =》 傳輸act
            =》 傳輸argc   
            =》 傳輸argv1
            =》 傳輸argv2
            ...
            =》 傳輸argvn (n==argc)
step3: 接受Autod的返回碼。這個返回碼是該動做執行的結果。0表示成功,其他表示失敗。
step4:銷燬socket,退出Auto。


詳細設計 -- Autod:
Autod做爲服務在後臺運行。
step1: Autod啓動,讀取config file中的信息,存到本身的數據結構中做爲緩存。
            =》若是失敗,直接退出,給出提示。
            =》若是成功,則轉到step2.
step2: Autod創建建立listen socket,監聽端口8008(好名字!),等待Auto的鏈接
step3: Autod接收到Auto的連接,建立一個client socket與之通訊。
            =》接受act
            =》接受argc
            =》接收argv1
            ...
            =》接收argvn (n==argc)
step4: 根據接受到的act和argc以及argv採起相應的動做。
            1) act == register
                => register program to cache
                => update config file
                => 根據update的結果返回給Auto返回碼。(若是I/O出問題,update失敗了,返回非0)
            2) act == deregister
                => deregister program in cache
                => update config file
                => send back return code to Auto according to the result of updating step
            3)  act == list
                => list all registered programs in cache
                => return 0
            4)  act == execute
                => execute the registered program (return code RET)
                => sent RET back to Auto
step5:  銷燬socket。轉到step2.


詳細設計 -- config file:
config file用來存儲已經註冊上的程序。形式以下:
name: prog1-name
path: prog1-path
desc: prog1-desc

name: prog2-name
...


數據結構-- Autod接受Auto請求的數據結構
char act[32];
int progargc;
char **progargv; //這裏會有動態內存的申請和釋放,要注意。
不須要將他們封裝成相似struct msg這種東西,這樣使得程序複雜化。


數據結構 -- Autod的緩存
struct item
{
    char progname[32];
    char path[128];
    char desc[128]; //限制desc的字數!
};
struct item[64];做爲緩存,不進行動態分配是由於這樣寫程序簡單。並且目測在至關長一段時間內,你的「有用的程序「都不會超過64個。


基礎小問題(就是說,請你先解決如下問題再寫程序!)
1. 正確讀寫config file (OK)
2. 如何得到某個文件的絕對路徑
3. 如何組織auto,autod和autod.conf的目錄。
4. 如何判斷指定文件是否存在,文件是否可執行?
5. 如何進行客戶端和服務端的收發同步?
6. 服務端執行程序後的輸出,如何傳給客戶端,讓它在客戶端的shell中顯示,就好像是客戶端在執行這個程序同樣?

(Note: Don't Be Lazy! Program defensively! People make mistakes! You make mistakes!)


Note:直到想到第六個基礎小問題,我才發現,這個設計不好!實際上經過config file存儲信息後,並不須要client-server這種交互了。直接用auto去和config file交互,而後同步就能夠了。徹底不須要autod!!一開始的設計,autod所執行的功能中,auto不能作的,只不過是個緩存而已!實際上,咱們並不須要緩存,直接auto去和文件交互就能夠了!!
實際上用auto<===>config file的方式就能完成任務了!!
典型的client server的錯誤分離,不只使得程序複雜化,並且使一些問題解決起來很麻煩。好比基礎小問題6.

後記:
寫完這個,我忽然想到,這個東西有點像windows下的快捷方式,而windows下的快捷方式很容易讓人聯想到Linux下的symbolic link。因而,我發現本身二了。其實,只須要在~/bin目錄下,對須要的程序創建軟鏈接就能夠了。另外,能夠在~/bin目錄下建立一個腳本,其功能是說明每一個文件的做用。之後增刪,只須要對次說明文件稍做修改便可。

體會:
三思然後行。對問題的準確分析很是重要。簡直過重要了!
我一開始對問題的分析不許確,致使我定了一個不合適的目標;不只如此,我還作了一個不合適的設計;致使的結果就是,作了一天無用功。我這簡直就是太后知後覺了。

聊書此文,引覺得戒。shell

 

最後附上本身的auto.c和autod.c的代碼,算是爲了完整吧。windows

/**
 * auto.c  --  client interface for auto
 *
 * auto only checks the validality of the options.
 * It does not check whether a program has been registered or not.
 * It does not check whether the program exists or not.
 * It checks the validality of options, passes necessary info to autod and then receives result from autod.
 **/

#include <getopt.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>		/* get hostip by hostname */

#define DEBUG 0

#define MAX_ARG_LEN 128
#define RET_BUF_SIZE 128

const char* program_name;	/* this program's name */

static char progname[MAX_ARG_LEN];		/* the attributes of the program */
static char progpath[MAX_ARG_LEN];
static char progdesc[MAX_ARG_LEN];
static char **progargv = NULL;		/* for execution of program */
static char *action = NULL;		/* "register" "deregister" "list" "execute" */
static int extra_arg_num = 0;

const int port = 8008;
const char *ip = "127.0.0.1";

#if DEBUG
#define PDEBUG(fmt, args...) printf(fmt, ##args)
#else
#define PDEBUG(fmt, args...)
#endif

static void get_name_from_path(char *name, char *path)
{
	int len = strlen(path);
	int i;
	for (i=len-1; i>=0; i--)
	{
		if (path[i] == '/')
			break;
	}
	strcpy(name, path+i+1);
}

void print_usage(FILE* stream, int exitcode)
{
	fprintf(stream, "usage: %s [action] \n", program_name);
	fprintf(stream, 
		"   -h  --help                       Display this infomation. \n"
		"   -r  --register progname          Register progname. \n"
		"   -m  --desc description           Describe a program. \n"
		"   -d  --deregister progname        Deregister progname. \n"
		"   -e  --exec progname              Execute a registered program\n"
		"   -l  --list                       List all registered programs. \n");
	fprintf(stream, "-r and -m must be used together! Other options cannot be used at the same time!\n");
	exit(exitcode);
}

static void parse_options(int argc, char **argv)
{
	int next_option;
	int opth=0, optr=0, optm=0, optd=0, opte=0, optl=0;

	const char* const short_options = "hr:m:d:e:l";
	const struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"register", 1, NULL, 'r'},
		{"desc", 1, NULL, 'm'},
		{"deregister", 1, NULL, 'd'},
		{"exec", 1, NULL, 'e'},
		{"list", 0, NULL, 'l'},
		{NULL, 0, NULL, 0}
	};

	program_name = argv[0];
	do
	{
		next_option = getopt_long(argc, argv, short_options, long_options, NULL);
		switch (next_option)
		{
		case 'h':
			print_usage(stdout, 0);
			break;
		case 'r':
			action = "register";
			if (strlen(optarg) > MAX_ARG_LEN-1)
			{
				fprintf(stderr, "Make your program name SHORT!\n");
				exit(EXIT_FAILURE);
			}
			realpath(optarg, progpath);
			get_name_from_path(progname, progpath);
			optr = 1;
			break;
		case 'm':
			if (strlen(optarg) > 127)
			{
				fprintf(stderr, "Make your program description SHORT!\n");
				exit(EXIT_FAILURE);
			}
			strcpy(progdesc, optarg);
			optm = 1;
			break;
		case 'd':
			action = "deregister";
			if (strlen(optarg) > MAX_ARG_LEN-1)
			{
				fprintf(stderr, "Make your program name SHORT!\n");
				exit(EXIT_FAILURE);
			}
			realpath(optarg, progpath);
			get_name_from_path(progname, progpath);
			optd = 1;
			break;
		case 'e':
			action = "execute";
			if (strlen(optarg) > MAX_ARG_LEN-1)
			{
				fprintf(stderr, "Make your program name SHORT!\n");
				exit(EXIT_FAILURE);
			}
			realpath(optarg, progpath);
			get_name_from_path(progname, progpath);
			opte = 1;
			break;
		case 'l':
			action = "list";
			optl = 1;
			break;
		case '?':
			print_usage(stderr, 1);
			break;
		case -1 :
			break;
		default:
			abort();
		}
	} while(next_option != -1);
	extra_arg_num = argc - optind;
	if (extra_arg_num < 0)
	{
		perror("Unexpected Error!");
		exit(EXIT_FAILURE);
	}
	else if (extra_arg_num == 0)
	{
		/* do nothing */
	}
	else			/* extra_arg_num > 0 */
	{
		progargv = (char **)malloc(sizeof(char*)*(extra_arg_num+1)); /* last progargv should be NULL */
		if (progargv == NULL)
		{
			perror("Not Enough Memory!");
			exit(EXIT_FAILURE);
		}
		/* init progargv[i] */
		int i=0;
		for (i=0; i<extra_arg_num+1; i++)
			progargv[i] = NULL;
		int j=0;
		PDEBUG("optind = %d, argc = %d \n", optind, argc); 
		for (i=optind; i<argc; i++)
		{
			progargv[j] = (char*)malloc(sizeof(char)*MAX_ARG_LEN);
			if (progargv[j] == NULL)
			{
				perror("Not Enough Memory!");
				exit(EXIT_FAILURE);
			}
			if (strlen(argv[i]) > 127)
			{
				fprintf(stderr, "Too LONG argument!\n");
				exit(EXIT_FAILURE);
			}
			strcpy(progargv[j], argv[i]);
			j++;
		}
	}
	/* check optx and progargv */
	/* only -r and -m could be used together */
	int sum = opth + optr + optm + optd + opte + optl;
	if (sum == 0)
	{
		/* you have not specified any action */
		print_usage(stdout, 0);
	}
	else if (sum == 1)
	{
		if ( (optr==1) || (optm==1) )
		{
			fprintf(stderr, "-r and -m must be used together!\n");
			exit(EXIT_FAILURE);
		}
		if (opte==0)
		{
			/* action is not "execute", progargv must be NULL */
			if (progargv != NULL)
			{
				fprintf(stderr, "Too Many arguments!\n");
				exit(EXIT_FAILURE);
			}
		}
	}
	else if (sum == 2)
	{
		if ( !(optr==1 && optm==1) )
		{
			fprintf(stderr, "Too Many options!\n");
			exit(EXIT_FAILURE);
		}
	}
	else
	{
       		fprintf(stderr, "Too Many options!\n");
       		exit(EXIT_FAILURE);
	}
}

/**
 * function: connect to autod
 * @return : return socket descriptor
 *
 **/
static int connect_to_autod(const char* ip, const int port)
{
	int ret;
	/* create a socket */
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("create socket failed: ");
		exit(EXIT_FAILURE);
	}
	/* create a connect to host */
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(ip);
	addr.sin_port = htons(port);
	ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
	if (ret < 0)
	{
		perror("connect failed: ");
		exit(EXIT_FAILURE);
	}
	else
	{
		PDEBUG("connect to %s:%d succeed \n", ip, port);
	}
	return sockfd;
}

/**
 * function: recv_from_autod
 *
 * receive return infomation from autod
 **/
static void recv_from_autod(int sockfd, char *ret_buf)
{
	int ret;
       	ret = read(sockfd, ret_buf, RET_BUF_SIZE);
	if (ret > 0)
	{
		PDEBUG("read [%4d] bytes: %s \n", ret, ret_buf);
	}
	else if (ret < 0)
	{
		perror("Read socket faild!");
		exit(EXIT_FAILURE);
	}
	else		/* ret == 0 */
	{
		PDEBUG("ret == 0\n");
	}
}

static void send_request(int sockfd, const char *request)
{
	int ret =  write(sockfd, request, strlen(request));
	if (ret < 0)
	{
		perror("send request failed: ");
		exit(EXIT_FAILURE);
	}
	else
	{
		PDEBUG("send %d bytes to host [%s]: %s \n", ret, ip, request);
	}
	/* receive confirm from autod: for synchronization purpose */
	if (strcmp(request, "sync")==0)
		return;
	char buf[RET_BUF_SIZE];
	memset(buf, 0, RET_BUF_SIZE);
	recv_from_autod(sockfd, buf);
}

/**
 * function: send_to_autod
 *
 * send action [progname] [description] [arguments] to autod, [] means optional
 **/
static void send_to_autod(int sockfd)
{
	int argc = 0;
	char strargc[10];
	/* send action */
	send_request(sockfd, action);
	/* send argc */
	/* send 'argv', 'argv' is actually [progname] + [desc] + [progargv] */
	if (strcmp(action, "register") == 0)
	{
		argc = 3;
		sprintf(strargc, "%d", argc);
		send_request(sockfd, strargc);
		send_request(sockfd, progname);
		send_request(sockfd, progpath);
		send_request(sockfd, progdesc);
	}
	else if (strcmp(action, "deregister") == 0)
	{
		argc = 1;
		sprintf(strargc, "%d", argc);
		send_request(sockfd, strargc);
		send_request(sockfd, progname);
	}
	else if (strcmp(action, "list") == 0)
	{
		argc = 0;
		sprintf(strargc, "%d", argc);
		send_request(sockfd, strargc);
	}
	else if (strcmp(action, "execute") == 0)
	{
		argc = 1 + extra_arg_num; /* progname + extra arguments */
		sprintf(strargc, "%d", argc);
		send_request(sockfd, strargc);
		send_request(sockfd, progname); /* we should send progname instead of a wrong progpath */
		int i=0;
		for (i=0; i<extra_arg_num; i++)
		{
			assert(progargv != NULL);
			assert(progargv[i] != NULL);
			send_request(sockfd, progargv[i]);
		}
	}
	else
	{
		fprintf(stderr, "Invalid Action! This should never happen!\n");
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char *argv[])
{
	int i;
	parse_options(argc, argv);

#if DEBUG
	if (action != NULL)
	{
		printf("action: %s\n", action);
		printf("progname: %s\n", progname);
		printf("progpath: %s\n", progpath);
		printf("progdesc: %s\n", progdesc);
	}
	if (progargv != NULL)
	{
		for (i=0; i<extra_arg_num; i++)
			printf("argv: %s\n", progargv[i]);
	}
#endif
	char ret_buf[RET_BUF_SIZE]; /* buffer to hold return info from autod */
	memset(ret_buf, 0, RET_BUF_SIZE);
	int sockfd;		/* client socket */

	sockfd = connect_to_autod(ip, port);
	send_to_autod(sockfd);	/* send request to auto */

	send_request(sockfd, "sync");
	recv_from_autod(sockfd, ret_buf);

	printf("\033[41;32;1m%s\033[0m\n", ret_buf);
	close(sockfd);
	exit(EXIT_SUCCESS);
}

/**
 * autod.c
 *
 * autod 
 **/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>		/* get hostip by hostname */

#define DEBUG 0

#define MAX_ARG_LEN 128
#define RET_BUF_SIZE 128

static char *progname;		/* the attributes of the program */
static char *progpath;
static char *progdesc;
static char **progargv = NULL;		/* for execution of program */
static char action[MAX_ARG_LEN];		/* "register" "deregister" "list" "execute" */
static int progargc = 0;		/* progargc = progname + [progargv] + [desc] */

const int port = 8008;

#if DEBUG
#define PDEBUG(fmt, args...) printf(fmt, ##args)
#else
#define PDEBUG(fmt, args...)
#endif

const char *config_file_path = "/etc/autod/autod.conf";

static void get_name_from_path(char *name, char *path)
{
	int len = strlen(path);
	int i;
	for (i=len-1; i>=0; i--)
	{
		if (path[i] == '/')
			break;
	}
	strcpy(name, path+i+1);
}

/**
 * struct item -- structure describing a program in autod.conf
 *
 **/
struct item
{
	char name[MAX_ARG_LEN];
	char path[MAX_ARG_LEN];
	char desc[MAX_ARG_LEN];
};

/**item cache**/
static struct item item_cache[64];
static int item_count = 0;	/* item number in item cache */

/**
 * function: read_field -- read field from config file
 * @fp : file stream for config file
 * @it: field in struct item, e.g. name, path
 * @fieldname: indicates which field to read, e.g. "name: ", "path: ", "desc: "
 * 
 **/
static int read_field(FILE *fp, char *it, const char* fieldname)
{
	assert(fp != NULL);
	char *ret = NULL;
	char buf[MAX_ARG_LEN];
	int len = strlen(fieldname);
	/* read from fp and check the line: whether it is "path: xxxx" */
	while (1)
	{
		ret = fgets(buf, MAX_ARG_LEN-1, fp);
		if (ret == NULL)
			return -1;
		else
		{
			if (strlen(buf) < len)
				continue; /* invaild line in config file */
			else		  /* check the line */
			{
				if (strncmp(buf, fieldname, len) == 0)
				{
					strcpy(it, buf+len);
					/* remove the last character \n in it */
					if ( it[strlen(it)-1] == '\n' )
						it[strlen(it)-1] = '\0';
					return 0;
				}
				else
				{
					fprintf(stderr, "\033[40;31;1mconfig file [%s] may be in a wrong state! Check it!\033[0m \n", config_file_path);
					return -1;
				}
			}
		}
	}
}

/**
 * function: read_item -- read an item from config file to item cache
 * @fp : file stream of config file
 * @it: item in item cache
 * @return : On success, 0; On failure or end of file, -1.
 *
 **/
static int read_item(FILE *fp, struct item *it)
{
	assert(fp != NULL);
	int ret = 0;
	ret = read_field(fp, it->name, "name: ");
	if (ret < 0)
		return ret;
	ret = read_field(fp, it->path, "path: ");
	if (ret < 0)
		return ret;
	ret = read_field(fp, it->desc, "desc: ");
	if (ret < 0)
		return ret;
	return 0;
}

static void fprint_item_cache(FILE* fp)
{
	int i=0;
	for (i=0; i<item_count; i++)
	{
		fprintf(fp, "name: %s\n", item_cache[i].name);
		fprintf(fp, "path: %s\n", item_cache[i].path);
		fprintf(fp, "desc: %s\n", item_cache[i].desc);
		fprintf(fp, "\n");
	}
}

/**
 * function: send_to_auto
 *
 * send back return message to auto ( "SUCCESS" or "FAIL" )
 **/
static void send_to_auto(int sockfd, const char *msg)
{
	int ret =  write(sockfd, msg, strlen(msg));
	if (ret < 0)
	{
		perror("send message failed: ");
		exit(EXIT_FAILURE);
	}
	else
	{
		PDEBUG("send to message to client succeed: %s \n", msg);
	}
}

static void recv_request(int sockfd, char *buf, int count)
{
	int ret;
       	ret = read(sockfd, buf, count);
	if (ret > 0)
	{
		PDEBUG("read [%4d] bytes: %s \n", ret, buf);
	}
	else if (ret < 0)
	{
		perror("Read socket faild!");
		exit(EXIT_FAILURE);
	}
	else		/* ret == 0 */
	{
		PDEBUG("ret == 0\n");
	}
	/* send to auto a confirmation message: for synchronization purpose */
	if (strcmp(buf, "sync") == 0) /* for sync purpose. BAD DESIGN!!!! REWRITE IT LATER!!! */
		return;
	send_to_auto(sockfd, "confirm");
}

/**
 * function: recv_from_auto
 *
 * receive requests from auto: action + [progname] + [progdesc] + [progargv]
 **/
static void recv_from_auto(int sockfd)
{
	char strargc[10];
	memset(strargc, 0, 10);

	recv_request(sockfd, action, MAX_ARG_LEN);
	recv_request(sockfd, strargc, 10);
	progargc = atoi(strargc);

	if (progargc == 0)
		return;

	/* progargc > 0, init progargv */
	progargv = (char **)malloc(sizeof(char*) * (progargc+1));
	if (progargv == NULL)
	{
		perror("not enough memory");
		exit(EXIT_FAILURE);
	}
	int i=0;
	for (i=0; i<progargc+1; i++)
	{
		progargv[i] = NULL;
	}
	for (i=0; i<progargc; i++)
	{
		progargv[i] = (char*)malloc(sizeof(char) * MAX_ARG_LEN);
		if (progargv[i] == NULL)
		{
			perror("Not Enough Memory");
			exit(EXIT_FAILURE);
		}
		memset(progargv[i], 0, MAX_ARG_LEN);
	}
	/* receive progargv[0] --- progargv[progargc-1] */
	for (i=0; i<progargc; i++)
	{
		recv_request(sockfd, progargv[i], MAX_ARG_LEN);
	}
}

static void sync_file()
{
	FILE *fp = NULL;
	fp = fopen(config_file_path, "w");
	fprint_item_cache(fp);
	fclose(fp);

}

/* move item_cache[src] to item_cache[dest] */
static void move_item(int dest, int src)
{
	strcpy(item_cache[dest].name, item_cache[src].name);
	strcpy(item_cache[dest].path, item_cache[src].path);
	strcpy(item_cache[dest].desc, item_cache[src].desc);
}

static void remove_item(int index)
{
	assert(index < item_count);
	int i;
	for (int i=index; i<item_count-1; i++)
		move_item(i, i+1);
	item_count--;
}
/**
 * function: deal request
 * @return : On success, return 0; On failure, return -1
 *
 * take corresponding action according to action, progargc and progargv
 **/
int deal_request(void)
{
	int i=0;
	int ret;
	FILE *fp = NULL;
#if DEBUG
	printf("action: %s\n", action);
	printf("progargc: %d\n", progargc);
	for (i=0; i<progargc; i++)
	{
		printf("argv: %s\n", progargv[i]);
	}
#endif
	if (strcmp(action, "register")==0)
	{
		progname = progargv[0];
		progpath = progargv[1];
		progdesc = progargv[2];

		/* check whether progpath is a file */
		ret = access(progpath, F_OK | X_OK);
		if (ret < 0)
			return -1;
		/* check whether progname has already in cache, if so, return -1 */
		for (i=0; i<item_count; i++)
		{
			if (strcmp(progname, item_cache[i].name)==0)
				return -1;
		}
		/* register it to cache */
		strcpy(item_cache[item_count].name, progname);
		strcpy(item_cache[item_count].path, progpath);
		strcpy(item_cache[item_count].desc, progdesc);
		item_count++;
		/* synchonize autod.conf */
		sync_file();
		return 0;
	}
	else if (strcmp(action, "deregister")==0)
	{
		progname = progargv[0];
		/* check whether progname has already in cache, if not, return -1 */
		for (i=0; i<item_count; i++)
		{
			if (strcmp(progname, item_cache[i].name)==0)
				break;
		}
		if (i == item_count)
			return -1;
		/* remove it from cache */
		remove_item(i);
		/* synchronize autod.conf */
		sync_file();
		return 0;
	}
	else if (strcmp(action, "list")==0)
	{
		/* print item_cache */
		fprint_item_cache(stdout);
	}
	else if (strcmp(action, "execute")==0)
	{
		progname = progargv[0];
		/* check whether progname has already in cache, if not, return -1 */
		for (i=0; i<item_count; i++)
		{
			if (strcmp(progname, item_cache[i].name)==0)
			{
				progpath = item_cache[i].path;
				break;
			}
		}
		if (i == item_count)
			return -1;

		/* if progname is in cache, execute it */
		/* progargv[0] = progname -> progpath */
		strcpy(progargv[0], progpath);

		int pid = fork();
		if (pid < 0)
		{
			perror("fork failed!");
			return -1;
		}
		else if (pid == 0) /* child process */
		{
			ret = execvp(progpath, progargv);
			if (ret < 0) /* execvp failed */
				return -1;
		}
		else		/* parent process */
		{
			wait(NULL);
			return 0;
		}
	}
	else			/* should never happen */
	{
		fprintf(stderr, "We should NEVER get here!\n");
		return -1;
	}
	return 0;
}

static void init_cache()
{
	int ret;
	FILE *fp;
	/* open config file */
	fp = fopen(config_file_path, "r");
	if (fp == NULL)
	{
		perror("open config file failed!");
		exit(EXIT_FAILURE);
	}
	/* read from config file to item_cache */
	do
	{
		ret = read_item(fp, &item_cache[item_count]);
		if (ret == 0)
			item_count++;
	} while (ret == 0);
	fclose(fp);
}

int main(int argc, char *argv[])
{
	FILE *fp = NULL;
	int ret;
	int i;

	init_cache();
	/* create a listen socket, listen to port 8008 */
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("create socket failed: ");
		exit(EXIT_FAILURE);
	}
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr(ip);
	addr.sin_port = htons(port);

	int len = sizeof(addr);
        ret = bind(sockfd, (struct sockaddr *)&addr, len);
	if (ret < 0)
	{
		perror("bind to port 8008 failed!");
		exit(EXIT_FAILURE);
	}
	ret = listen(sockfd, 5);
	if (ret < 0)
	{
		perror("listen port 8008 failed!");
		exit(EXIT_FAILURE);
	}

	int client_sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (client_sockfd == -1)
	{
		perror("create client socket failed: ");
		exit(EXIT_FAILURE);
	}

	for (;;)
	{
		/* create a client socket */
		client_sockfd = accept( sockfd, NULL, NULL );
		if (client_sockfd < 0)
		{
			perror("accept error!");
			continue;
		}

		/* receive necessary info from auto */
		recv_from_auto(client_sockfd);
		/* take corresponding action according to requests */
		ret = deal_request();
		/* send back return message */
		char buftmp[10];
		memset(buftmp, 0, 10);
		recv_request(client_sockfd, buftmp, 10); /* for synchronization purpose */
		if (ret == 0)	/* success */
		{
			send_to_auto(client_sockfd, "SUCCESS");
		}
		else		/* fail */
		{
			send_to_auto(client_sockfd, "FAIL");
		}
		/* close client socket */
		close(client_sockfd);
		/* reset all resources */
		memset(action, 0, MAX_ARG_LEN);
		if (progargv != NULL)
		{
			for (i=0; i<progargc; i++)
			{
				if (progargv[i] != NULL)
					free(progargv[i]);
			}
			free(progargv);
			progargv = NULL;
		}
	}
	exit(EXIT_SUCCESS);
}
相關文章
相關標籤/搜索