聊書此文,引覺得戒。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); }