/* client.c CLIENT FIXME - perror() --- replace with log_it() 06/19 - fixed bugs in mondoarchive compare code - added track_restore_task_progress() 06/16 - when calling mondoarchive in bkgd, see if it starts OK; if it doesn't then say so & return error 06/14 - added a FIFO to let user request backup/compare/restore - send progress info to server - % done, etc. - when backing up - pipe logs to logfile, not stdout 06/11 - added call to register_pid() - commented code a bit - implemented compare_archives and restore_archives() 06/10 - create function to call external executable in background - create function to wait for it to terminate & to grab its result - put them in common.c 05/27 - fixed watch_port_for_triggers() - turned st_'s into a global and some locals - fork in login thingy becomes a thread - added bind_client_port() - added accept_and_recv_thru_client_port() - changed flag (recv) from DONTWAIT to 0 [no flag] 05/21 - added back_my_smitch_up(), compare_archives(), restore_archives() - added log_it(); fixed fprintf(stderr,"") and printf() reporting - parallelize/fork the 'watch for triggers from server' process - added tmsg_to_string() - forked port-watcher to receive triggers from server in bkgd 05/11 - clarified structures & their names - improved login/logout OK/fail feedback 05/08 - did some housecleaning - added comments; removed strcpy()'s - replaced silly exit()'s with return()'s */ #include "structs.h" //#define LOG_THESE_AND_HIGHER debug //#define LOGFILE "/var/log/monitas-client.log" /* global vars */ bool g_logged_in_currently=false, g_logging_out=false; int g_sClient=-1, g_client_port=0; /* client port; set by login */ struct sockaddr_in g_sinClient; /* client port */ char g_server_name[MAX_STR_LEN+1]; pthread_t g_mondo_thread=0; char g_command_fifo[MAX_STR_LEN+1]; /* externs */ extern char *call_program_and_get_last_line_of_output(char*); extern int call_program_and_log_output(char*); extern void call_program_in_background(pthread_t*, char*); extern int create_and_watch_fifo_for_commands(char*); extern bool does_file_exist(char*); extern int get_bkgd_prog_result(pthread_t*); extern void log_it_SUB(char*, t_loglevel level, char *sz_message); extern bool program_still_running(char*); extern int receive_file_from_socket(FILE*, int); extern void register_pid(pid_t, char*); extern char *tmsg_to_string(t_msg msg_type); extern int transmit_file_to_socket(FILE*, int); extern void register_pid(pid_t, char*); extern void set_signals(bool); extern int parse_options(int argc, char *argv[]); /* prototypes */ int accept_and_recv_thru_client_port(int, int*, struct sockaddr_in*, char*, int); int back_my_smitch_up(char*, int); int bind_client_port(struct sockaddr_in*, int); int compare_archives(char*, int); int find_and_bind_free_server_port(struct sockaddr_in*, int*); long increment_magic_number(void); int login_to_server(char*,char*); void logout_and_exit(char*); int logout_of_server(char*); int process_incoming_command(char*); int restore_archives(char*, char*, int); void restore_archives_SIGPIPE(int); int send_final_progress_report(char*); int send_msg_to_server(struct s_client2server_msg_record*, char*); int send_ping_to_server(char*,char*); int send_progress_rpt_to_server(char*, char*); void terminate_daemon(int); void *track_backup_task_progress(void*); void *track_compare_task_progress(void*); void *track_restore_task_progress(void*); void *watch_port_for_triggers_from_server(void*); /*-----------------------------------------------------------*/ int accept_and_recv_thru_client_port(int sClient, int *new_sClient, struct sockaddr_in*sinClient, char*incoming, int expected_length) /* Purpose:Run accept() and recv() to open port and receive message from server. Params: sClient - file descriptor of port new_sClient - [returned] file descriptor of the new connection to port which we open in this func sinClient - record about port expected_length - expected length of incoming block Return: length of block received, or <0 if error */ { int len; len = sizeof(struct sockaddr_in); if ((*new_sClient = accept(sClient, (struct sockaddr*)sinClient, (unsigned int*)&len)) < 0) { log_it(error, "[child] Cannot accept"); return(-6); } if ((len = recv(*new_sClient, incoming, expected_length, /*MSG_DONTWAIT*/0)) <= 0) { log_it(error, "[child] Cannot recv"); return(-7); } return(len); } /*-----------------------------------------------------------*/ char *get_param_from_rcfile(char*fname, char*field) { char command[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1]; static char sz_res[MAX_STR_LEN+1]; sz_res[0]='\0'; if (does_file_exist(fname)) { sprintf(command, "cat %s | grep %s= | cut -d'=' -f2,3,4,5,6,7,8,9", fname, field); strcpy(tmp, call_program_and_get_last_line_of_output(command)); strcpy(sz_res, tmp); } return(sz_res); } int back_my_smitch_up(char*msgbody, int socket_fd) /* Purpose:Backup archives to server. Params: msgbody - char[MSG_BODY_SIZE] containing info about the archives to be created socket_fd - file descriptor to which to write the archives to server. Return: result (0=success; nonzero=failure) */ { char tmp[MAX_STR_LEN+1], command[MAX_STR_LEN+1], tempdev[MAX_STR_LEN+1]; char temporary_logfile[MAX_STR_LEN+1]; // where mondoarchive writes its stdout,stderr char mondoparams_str[MAX_STR_LEN+1]; struct s_server2client_msg_record incoming_rec; int retval=0, len, new_sClient, res=0; FILE*fin; pthread_t progress_thread; sprintf(tmp, "Backup of %s commenced", msgbody); log_it(info, tmp); if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Unable to send 'yep, got this msg' progress_rpt to server"); } sprintf(temporary_logfile, "/tmp/monitas-client.templog.%d", (int)random()%32767); sprintf(tempdev, "/tmp/monitas-client.device.%d", (int)random()%32767); unlink(tempdev); if (mkfifo(tempdev, 700)) { log_it(error, "Unable to create temporary data output fifo in preparation for the call to mondoarchive"); return(1); } unlink(temporary_logfile); if (mkfifo(temporary_logfile, 700)) { log_it(error, "Unable to create temporary logfile fifo in preparation for the call to mondoarchive"); return(1); } strcpy(mondoparams_str, get_param_from_rcfile(g->client_rcfile, "mondoarchive_params")); sprintf(tmp, "mondoarchive_params --> '%s'", mondoparams_str); log_it(debug, tmp); sprintf(command, "mondoarchive -Ou %s -d %s -I %s -F &> %s; rm -f %s %s", mondoparams_str, tempdev, msgbody, temporary_logfile, tempdev, temporary_logfile); call_program_in_background(&g_mondo_thread, command); sleep(10); if (!program_still_running(command)) { res=1; log_it(error, "Unable to start mondoarchive. Please check /var/log/mondo-archive.log"); } else { if (pthread_create(&progress_thread, NULL, track_backup_task_progress, (void*)temporary_logfile)) { log_it(error, "Cannot create pthread to track mondo task progress"); return(1); } log_it(debug, "Opening fopen() to tempdev"); if (!(fin = fopen(tempdev, "r"))) { log_it(error, "Cannot open FIFO"); return(1); } log_it(debug, "fopen() OK"); retval = transmit_file_to_socket(fin, socket_fd); fclose(fin); res = get_bkgd_prog_result(&g_mondo_thread); pthread_join(progress_thread, NULL); } if (res) { retval++; log_it(error, "Mondoarchive returned an error. Notifying server..."); } if (res) { retval++; log_it(error, "Mondoarchive returned an error."); } if (retval) { log_it(debug, "Errors have occurred. Notifing server..."); } else { log_it(debug, "Everything is OK so far. Notifying server..."); } if (write(socket_fd, (char*)&retval, sizeof(retval))!=sizeof(retval)) {retval++;} /* receive msg from server; did backup go OK at its end or not? */ unlink(tempdev); unlink(temporary_logfile); log_it(debug, "Waiting for progress thread to join us"); log_it(debug, "Groovy. Continuing.."); len = accept_and_recv_thru_client_port(g_sClient, &new_sClient, &g_sinClient, (char*)&incoming_rec, sizeof(incoming_rec)); if (len<0) { log_it(error, "After backup, unable to accept/recv thru client port"); return(-10); } sprintf(tmp, "After backup, I received %s - %s", tmsg_to_string(incoming_rec.msg_type), incoming_rec.body); log_it(debug, tmp); if (incoming_rec.msg_type == backup_fail) { retval++; log_it(error, "Server reported error(s) during backup, although client didn't."); } if (retval) { sprintf(tmp, "Backup of %s failed", msgbody); log_it(error, tmp); call_program_and_log_output("tail -n6 /var/log/mondo-archive.log"); } else { sprintf(tmp, "Server agrees, backup of %s succeeded :-)", msgbody); log_it(info, tmp); } if (send_final_progress_report(tmp) < 0) { retval++; log_it(error, "Unable to send final progress_rpt to server"); } return(retval); } /*-----------------------------------------------------------*/ int bind_client_port(struct sockaddr_in *sinClient, int client_port) /* Purpose:Bind one of my ports so that I may open it later and write/read data to/from server with it. Params: sinClient - record/structure relating to the socket client_port - port# to be bound Return: socket handle, to be used by other subroutines, if success or <0 if failure */ { int sClient=-1; char tmp[MAX_STR_LEN+1]; memset((void*)sinClient, 0, sizeof(struct sockaddr_in)); sinClient->sin_family = AF_INET; sinClient->sin_addr.s_addr = INADDR_ANY; sinClient->sin_port = htons(client_port); if ((sClient = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { sprintf(tmp, "Unable to open socket on port #%d", g_client_port); log_it(error, tmp); return(-1); } if (bind(sClient, (struct sockaddr*)sinClient, sizeof(struct sockaddr_in)) < 0) { sprintf(tmp, "Cannot bind %d - %s", g_client_port, strerror(errno)); log_it(error, tmp); return(-2); } if (listen(sClient, MAX_PENDING) < 0) { sprintf(tmp, "Cannot setup listen (%d) - %sn", g_client_port, strerror(errno)); log_it(error, tmp); return(-3); } log_it(debug, "Successfully bound client port."); return(sClient); } /*-----------------------------------------------------------*/ int compare_archives(char*msgbody, int socket_fd) /* Purpose:Compare archives, sent by server. Params: msgbody - char[MSG_BODY_SIZE] containing info about the archives to be compared socket_fd - file descriptor from which to read the archives sent by server to be compared. Return: result (0=success; nonzero=failure) */ { char tmp[MAX_STR_LEN+1], command[MAX_STR_LEN+1], tempdev[MAX_STR_LEN+1], *p; char temporary_logfile[MAX_STR_LEN+1]; // where mondoarchive writes its stdout,stderr char mondoparams_str[MAX_STR_LEN+1]; struct s_server2client_msg_record incoming_rec; int retval=0, len, new_sClient, res=0; FILE*fout, *fin; long diffs=0; pthread_t progress_thread; sprintf(tmp, "Comparison of %s commenced", msgbody); log_it(info, tmp); if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Unable to send 'yep, got this msg' progress_rpt to server"); } sprintf(temporary_logfile, "/tmp/monitas-client.templog.%d", (int)random()%32767); sprintf(tempdev, "/tmp/monitas-client.device.%d", (int)random()%32767); unlink(tempdev); if (mkfifo(tempdev, 700)) { log_it(error, "Unable to create temporary fifo in preparation for the call to mondoarchive"); return(1); } unlink(temporary_logfile); if (mkfifo(temporary_logfile, 700)) { log_it(error, "Unable to create temporary logfile fifo in preparation for the call to mondoarchive"); return(1); } strcpy(mondoparams_str, get_param_from_rcfile(g->client_rcfile, "mondoarchive_params")); sprintf(tmp, "mondoarchive_params --> '%s'", mondoparams_str); log_it(debug, tmp); sprintf(command, "mondoarchive -Vu -F %s -d %s -I %s > %s"/*; rm -f %s %s"*/, mondoparams_str, tempdev, msgbody, temporary_logfile/*, tempdev, temporary_logfile*/); call_program_in_background(&g_mondo_thread, command); sleep(5); if (!program_still_running(command)) { res=1; log_it(error, "Unable to start mondoarchive. Please check /var/log/mondo-archive.log"); } else { if (pthread_create(&progress_thread, NULL, track_compare_task_progress, (void*)temporary_logfile)) { log_it(error, "Cannot create pthread to track mondo task progress"); return(1); } fout = fopen(tempdev, "w"); log_it(debug, "Opened fopen() to tempdev"); retval = receive_file_from_socket(fout, socket_fd); log_it(debug, "Calling get_bkgd_prog_result"); fclose(fout); res = get_bkgd_prog_result(&g_mondo_thread); res = 0; // *shrug* Seems to help :) pthread_join(progress_thread, NULL); } if (res) { retval++; log_it(error, "Mondoarchive returned an error."); } if (retval) { log_it(debug, "Errors have occurred. Notifing server..."); } else { log_it(debug, "Everything is OK so far. Notifying server..."); } if (write(socket_fd, (char*)&retval, sizeof(retval))!=sizeof(retval)) {retval++;} /* receive msg from server; did comparison go OK at its end or not? */ unlink(tempdev); unlink(temporary_logfile); len = accept_and_recv_thru_client_port(g_sClient, &new_sClient, &g_sinClient, (char*)&incoming_rec, sizeof(incoming_rec)); if (len<0) { log_it(error, "After comparison, unable to accept/recv thru client port"); return(-10); } sprintf(tmp, "After comparison, I received %s - %s", tmsg_to_string(incoming_rec.msg_type), incoming_rec.body); log_it(debug, tmp); if (incoming_rec.msg_type == compare_fail) { retval++; log_it(error, "Server reported error(s) during comparison, although client didn't."); } if (retval) { sprintf(tmp, "Errors occurred during comparison of %s", msgbody); strcpy(command, call_program_and_get_last_line_of_output("tail -n20 /var/log/mondo-archive.log | grep /tmp/changed | head -n1")); p = strstr(command, "/tmp/changed"); if (p) { strcat(command, " "); sprintf(tmp, "command = '%s'", command); log_it(debug, tmp); *(strchr(p, ' '))='\0'; sprintf(tmp, "Opening list of changed files ('%s')", p); log_it(debug, tmp); log_it(info, "---Changed files---"); if ((fin=fopen(p, "r"))) { for(diffs=0; !feof(fin); diffs++) { fgets(tmp, MAX_STR_LEN, fin); if (strlen(tmp)>0) {tmp[strlen(tmp)-1]='\0';} log_it(info, tmp); } fclose(fin); } log_it(info, "----End of list----"); sprintf(tmp, "%ld differences were found during comparison of %s", diffs, msgbody); log_it(warn, tmp); unlink(p); } else { sprintf(tmp, "Errors occurred during comparison of %s", msgbody); log_it(error, tmp); call_program_and_log_output("tail -n6 /var/log/mondo-archive.log"); log_it(info, "Please check /var/log/mondo-archive.log for more information"); } } else { sprintf(tmp, "Server agrees, comparison of %s succeeded :-)", msgbody); log_it(info, tmp); } if (send_final_progress_report(tmp)) { retval++; log_it(error, "Unable to send final progress_rpt to server"); } return(retval); } /*-----------------------------------------------------------*/ int find_and_bind_free_server_port(struct sockaddr_in *sin, int *p_s) /* Purpose:Find a free port on the server. Bind to it, so that whichever subroutine called me can then send data to the server. Params: sin - server's IP address in a structure p_s - [return] file descriptor of port binding Return: result (>0=success, -1=failure) */ { int server_port; char tmp[MAX_STR_LEN+1]; if ((*p_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { // perror("socket"); log_it(info, "Creating socket failed: %s", strerror(errno)); return(-1); } for(server_port = 8700; server_port < 8710; server_port++) { sin->sin_port = htons(server_port); if (connect(*p_s, (struct sockaddr*)sin, sizeof(struct sockaddr_in)) < 0) { sprintf(tmp, "Not connecting at %d", server_port); log_it(debug, tmp); continue; } return(server_port); } close(*p_s); return(-1); } /*-----------------------------------------------------------*/ long increment_magic_number() /* Purpose:Increment the magic number which is attached to each message sent from client to server, to make the packet unique. Params: none Return: magic number */ { static unsigned long magic=1; magic=(magic % 999999999) + 1; return(magic); } /*-----------------------------------------------------------*/ int login_to_server(char*hostname, char*servername) /* Purpose:Ask server to log me (client) in. Params: hostname - client's hostname (not IP address necessarily but it should resolve to it) servername - server's hostname Return: result (-1=failure, N=client's port #) NB: The client's port # is chosen at random by send_msg_to_server and returned to me. */ { struct s_client2server_msg_record orig_rec; orig_rec.msg_type = login; strncpy(orig_rec.body, hostname, sizeof(orig_rec.body)); if (send_msg_to_server(&orig_rec, servername) < 0) { return(-1); } else { return(orig_rec.port); } } /*-----------------------------------------------------------*/ void logout_and_exit(char*servername) /* Purpose:Logout of server. Terminate. Params: servername - ip address of server Return: none */ { if (g_logged_in_currently) { if (logout_of_server(servername)) { log_it(warn, "Failed to logout of server."); } } call_program_and_log_output("rm -Rf /tmp/monitas-client.*"); register_pid(0, "client"); // chmod(g_command_fifo, 0); unlink(g_command_fifo); log_it(info, "---------- Monitas (client) has terminated ----------"); exit(0); } /*-----------------------------------------------------------*/ int logout_of_server(char*servername) /* Purpose:Instruct server to log client out. Params: servername - hostname of server Return: result (0=success; nonzero=failure) */ { struct s_client2server_msg_record orig_rec; g_logging_out = true; log_it(debug, "Logging out of server"); orig_rec.msg_type = logout; strncpy(orig_rec.body, "Bye bye!", sizeof(orig_rec.body)); if (send_msg_to_server(&orig_rec, servername) < 0) { return(1); } else { return(0); } } /*-----------------------------------------------------------*/ int process_incoming_command(char*incoming) /* Purpose:Process incoming command, presumably read from FIFO and sent there by sysadm/user. Params: incoming - raw command string itself Return: result (0=success; nonzero=failure) */ { int res=0; char tmp[MAX_STR_LEN+1]; int pos, i; char command[MAX_STR_LEN+1], path[MAX_STR_LEN+1], aux[MAX_STR_LEN+1]; struct s_client2server_msg_record orig_rec; pos=0; sscanf(incoming, "%s %s", command, path); for(i=0; i %s", tempdev, msgbody, msgbodyAux, temporary_logfile); // sprintf(command, "cat %s > %s", tempdev, "/tmp/out.dat"); call_program_in_background(&g_mondo_thread, command); sleep(5); if (!program_still_running(command)) { res=1; log_it(error, "mondorestore could not be started. Please check /tmp/mondo-restore.log"); } else { if (pthread_create(&progress_thread, NULL, track_restore_task_progress, (void*)temporary_logfile)) { log_it(error, "Cannot create pthread to track mondo task progress"); return(1); } fout = fopen(tempdev, "w"); log_it(debug, "Opened fopen() to tempdev"); retval = receive_file_from_socket(fout, socket_fd); if (retval && !system("cat /tmp/mondo-restore.log | grep -i \"End of restore_live_from_monitas_server\" &> /dev/null")) { retval=0; log_it(debug, "SIGPIPE caught but that's OK, it was anticipated."); res=0; } log_it(debug, "Calling get_bkgd_prog_result"); fclose(fout); res = get_bkgd_prog_result(&g_mondo_thread); pthread_join(progress_thread, NULL); } unlink(tempdev); unlink(temporary_logfile); if (res) { retval++; log_it(error, "mondorestore returned an error."); } if (retval) { log_it(debug, "Errors have occurred. Notifing server..."); } else { log_it(debug, "Everything is OK so far. Notifying server..."); } sleep(1); // probably unnecessary /* I do this thrice because mondorestore often causes a SIGPIPE, which means... I have to do this thrice :-) */ if (write(socket_fd, (char*)&retval, sizeof(retval))!=sizeof(retval)) {log_it(debug, "Failed to write wtf-info"); retval++;} if (write(socket_fd, (char*)&retval, sizeof(retval))!=sizeof(retval)) {log_it(debug, "Failed to write wtf-info"); retval++;} if (write(socket_fd, (char*)&retval, sizeof(retval))!=sizeof(retval)) {log_it(debug, "Failed to write wtf-info"); retval++;} /* receive msg from server; did restoration go OK at its end or not? */ unlink(tempdev); unlink(temporary_logfile); len = accept_and_recv_thru_client_port(g_sClient, &new_sClient, &g_sinClient, (char*)&incoming_rec, sizeof(incoming_rec)); if (len<0) { log_it(error, "After restoration, unable to accept/recv thru client port"); return(-10); } sprintf(tmp, "After restoration, I received %s - %s", tmsg_to_string(incoming_rec.msg_type), incoming_rec.body); log_it(debug, tmp); if (incoming_rec.msg_type == restore_fail) { retval++; log_it(error, "Server reported error(s) during restoration, although client didn't."); } if (retval) { sprintf(tmp, "Errors occurred during restoration of %s", msgbody); strcpy(command, call_program_and_get_last_line_of_output("tail -n20 /var/log/mondo-archive.log | grep /tmp/changed | head -n1")); p = strstr(command, "/tmp/changed"); if (p) { strcat(command, " "); sprintf(tmp, "command = '%s'", command); log_it(debug, tmp); *(strchr(p, ' '))='\0'; sprintf(tmp, "Opening list of changed files ('%s')", p); log_it(debug, tmp); log_it(info, "---Changed files---"); if ((fin=fopen(p, "r"))) { for(diffs=0; !feof(fin); diffs++) { fgets(tmp, MAX_STR_LEN, fin); if (strlen(tmp)>0) {tmp[strlen(tmp)-1]='\0';} log_it(info, tmp); } fclose(fin); } log_it(info, "----End of list----"); sprintf(tmp, "%ld differences were found during restoration of %s", diffs, msgbody); log_it(warn, tmp); } else { sprintf(tmp, "Errors occurred during restoration of %s", msgbody); log_it(error, tmp); call_program_and_log_output("tail -n6 /var/log/mondo-archive.log"); log_it(info, "Please check /var/log/mondo-archive.log for more information"); } } else { sprintf(tmp, "Server agrees, restoration of %s succeeded :-)", msgbody); log_it(info, tmp); } if (send_final_progress_report(tmp)) { retval++; log_it(error, "Unable to send final progress_rpt to server"); } return(retval); } /*-----------------------------------------------------------*/ void *send_final_progress_report_SUB(void*inp) { char message[MAX_STR_LEN+1]; strncpy(message, (char*)inp, MAX_STR_LEN); sleep(10); send_progress_rpt_to_server(g_server_name, message); pthread_exit(NULL); } int send_final_progress_report(char*final_string) { pthread_t thread; if (send_progress_rpt_to_server(g_server_name, final_string)) { return(1); } if (pthread_create(&thread, NULL, send_final_progress_report_SUB, (void*)"Idle")) { log_it(error, "Unable to create pthread"); return(1); } return(0); } /*-----------------------------------------------------------*/ int send_msg_to_server(struct s_client2server_msg_record *rec, char *servername) /* Purpose:Send message to server - a login/logout/ping request or perhaps a request for data to be restored. Params: rec - the message to be sent to server servername - the hostname of server Return: result (<0=failure, 0+=success) */ { struct hostent *hp; struct sockaddr_in sin; int server_port, new_sClient, s, len, res; struct s_server2client_msg_record incoming_rec; struct pollfd ufds; char tmp[MAX_STR_LEN+1]; void *thread_result; pthread_t a_thread; /* If logging out then kill the trigger-watcher before trying; otherwise, the trigger-watcher will probably catch the 'logout_ok' packet and go nutty on us :-) */ if ((hp = gethostbyname(servername)) == NULL) { sprintf(tmp, "%s: unknown host", servername); log_it(error, tmp); return(-1); } if (g_logged_in_currently && rec->msg_type == login) { log_it(error, "Already logged in. Why try again?"); return(-1); } if (!g_logged_in_currently && rec->msg_type != login) { log_it(fatal, "Server has forcibly logged you out."); } /* or you never logged in to begin which, which suggests the programmer screwed up */ /* open client port if login */ if (rec->msg_type == login) { log_it(info, "Trying to login"); g_client_port = 8800 + rand()%100; // FIXME: retry if I can't use this port g_sClient = bind_client_port(&g_sinClient, g_client_port); if (g_sClient<0) { log_it(error, "Cannot bind client port"); return(-1); } } /* send msg to server */ rec->port = g_client_port; memset((void*)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy((void*)&sin.sin_addr, hp->h_addr, hp->h_length); server_port = find_and_bind_free_server_port(&sin, &s); if (server_port<=0) { sprintf(tmp, "Cannot find+bind free server port. Is server running?"); log_it(error, tmp); return(-3); } rec->magic = increment_magic_number(); send (s, (char*)rec, sizeof(struct s_client2server_msg_record), 0); close(s); /* wait for ack/pong/feedback (if logging in/out) */ if (rec->msg_type == login /* || rec->msg_type == logout */) { ufds.fd = g_sClient; ufds.events = POLLIN|POLLPRI; poll(&ufds, 1, 1000); if (!ufds.revents) { log_it(error, "Failed to poll"); return(-5); } len = accept_and_recv_thru_client_port(g_sClient, &new_sClient, &g_sinClient, (char*)&incoming_rec, sizeof(incoming_rec)); if (len<0) { log_it(error, "Unable to accept/recv thru client port"); return(-10); } sprintf(tmp, "Received %s - %s", tmsg_to_string(incoming_rec.msg_type), incoming_rec.body); log_it(info, tmp); if (rec->msg_type != login && incoming_rec.msg_type == login_ok) { log_it(error, "WTF? login_ok but I wasn't logging in"); } if (rec->msg_type != logout&& incoming_rec.msg_type == logout_ok){ log_it(error, "WTF? logout_ok but I wasn't logging out"); } close(new_sClient); new_sClient=-1; } if (incoming_rec.msg_type == backup_fail) { log_it(error, "Failed, says server"); return(-8); } /* fork the process which watches in the background for pings/requests/etc. */ if (rec->msg_type == login) { g_logged_in_currently = true; strcpy(tmp, "Hello world!"); res = pthread_create(&a_thread, NULL, watch_port_for_triggers_from_server, (void*)tmp); log_it(debug, "Returning from login + call to pthread_create"); return(server_port); } if (rec->msg_type == logout) { log_it(debug, "Calling pthread_join to reunite trigger-watcher and main loop"); res = pthread_join(a_thread, &thread_result); if (res) { /*perror("Thread join failed");*/ log_it(debug, "Thread join failed in send_msg_to_server()"); } else { sprintf(tmp, "Thread join succeeded. Result: %s", (char*)thread_result); log_it(debug, tmp); } } return(0); } /*-----------------------------------------------------------*/ int send_ping_to_server(char*servername, char*msg) /* Purpose:Send a 'ping' to server. Params: msg - string to send ("Hello world"?) servername - server's hostname Return: result (0=success, nonzero=failure) */ { struct s_client2server_msg_record orig_rec; orig_rec.msg_type = ping; strncpy(orig_rec.body, msg, sizeof(orig_rec.body)); if (send_msg_to_server(&orig_rec, servername) < 0) { return(1); } else { return(0); } } /*-----------------------------------------------------------*/ int send_progress_rpt_to_server(char*servername, char*msg) /* Purpose:Send a 'progress_rpt' string to server. Params: msg - string to send ("Hello world"?) servername - server's hostname Return: result (0=success, nonzero=failure) */ { struct s_client2server_msg_record orig_rec; orig_rec.msg_type = progress_rpt; strncpy(orig_rec.body, msg, sizeof(orig_rec.body)); if (send_msg_to_server(&orig_rec, servername) < 0) { return(1); } else { return(0); } } /*-----------------------------------------------------------*/ void terminate_daemon(int sig) /* Purpose: Shut down the server in response to interrupt. Params: Signal received. Returns: None */ { int res; // FIXME - takes server 1-2 mins to realize I've aborted. I want that to be 5-10 seconds :) set_signals(false); // termination in progress log_it(info, "Abort signal caught by interrupt handler"); call_program_and_log_output("kill `ps ax | grep mondo | grep -v \"ps ax\" | grep -v \"grep mondo\" | grep monitas | cut -d' ' -f1`"); if (send_ping_to_server(g_server_name, "I'm pinging you before I logout")) { log_it(error, "Server has shut down without warning."); } if (g_mondo_thread) { res = get_bkgd_prog_result(&g_mondo_thread); } logout_and_exit(g_server_name); } /*-----------------------------------------------------------*/ void *track_backup_task_progress(void*inp) { char fname[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1], progress_str[MAX_STR_LEN+1], old_progstr[MAX_STR_LEN+1], *p; struct s_client2server_msg_record rec; FILE*fin; bool biggies=false, regulars=false; int prev_percentage=0, i; rec.msg_type = progress_rpt; strcpy(fname, (char*)inp); log_it(debug, "track_backup_task_progres() --- entering"); fin = fopen(fname, "r"); old_progstr[0]='\0'; sprintf(tmp, "fname = %s", fname); log_it(debug, tmp); sleep(2); if (!does_file_exist(fname)) { log_it(fatal, "track_backup_task_progress() -- fname not found"); } strcpy(rec.body, "Working"); if (send_msg_to_server(&rec, g_server_name) < 0) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } log_it(debug, rec.body); /* skip initial stuff */ while(!feof(fin)) { fgets(progress_str, MAX_STR_LEN, fin); progress_str[MAX_STR_LEN]='\0'; if (strlen(progress_str)<2) { continue; } if (strstr(progress_str, "TASK")) { break; } } /* skip more stuff */ while(!feof(fin)) { fgets(progress_str, MAX_STR_LEN, fin); progress_str[MAX_STR_LEN]='\0'; if (strlen(progress_str)<2) { continue; } if (!strstr(progress_str, "TASK")) { break; } } /* report on regular+biggie files */ while(!feof(fin)) { fgets(progress_str, MAX_STR_LEN, fin); progress_str[MAX_STR_LEN]='\0'; if (strstr(progress_str, "rchiving set 0")) { prev_percentage = 0; regulars = true; } if (strstr(progress_str, "acking up all large files")) { prev_percentage = 0; regulars = false; biggies = true; } if (strlen(progress_str)<2) { continue; } if (!strstr(progress_str, "TASK")) { continue; } progress_str[strlen(progress_str)-1] = '\0'; log_it(debug, progress_str); if (!biggies && !regulars) { strcpy(progress_str, "Still working..."); } if (strcmp(progress_str, old_progstr)) { strcpy(old_progstr, progress_str); if (biggies) { sprintf(rec.body, "Large files: %s", progress_str+6); } else if (regulars) { sprintf(rec.body, "Regular files: %s", progress_str+6); } if (!(p=strstr(progress_str, "% done"))) { continue; } log_it(info, rec.body); if (send_progress_rpt_to_server(g_server_name, rec.body)) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } continue; while(!isdigit(*(p-1))) { p--; } strcpy(tmp, p); *(strchr(tmp, '%')) = '\0'; i = atoi(tmp); if (i > prev_percentage) { prev_percentage = i; log_it(info, rec.body); // FIXME - could simply call send_progress_rpt_to_server() if (send_msg_to_server(&rec, g_server_name) < 0) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } } } } log_it(debug, "track_backup_task_progress() --- leaving"); pthread_exit(NULL); } /*-----------------------------------------------------------*/ void *track_compare_task_progress(void*inp) { char fname[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1], progress_str[MAX_STR_LEN+1], old_progstr[MAX_STR_LEN+1], *p; // struct s_client2server_msg_record rec; FILE*fin; bool biggies=false, regulars=false; int prev_percentage=0, i; strcpy(fname, (char*)inp); log_it(debug, "track_compare_task_progress() --- entering"); if (!(fin = fopen(fname, "r"))) { log_it(fatal, "Unable to openin tempdev while comparing"); } old_progstr[0]='\0'; sprintf(tmp, "fname = %s", fname); log_it(debug, tmp); sleep(2); if (!does_file_exist(fname)) { log_it(fatal, "track_compare_task_progress() -- fname not found"); } log_it(debug, "Working"); if (send_progress_rpt_to_server(g_server_name, "Working")) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } /* report on regular+biggie files */ while(!feof(fin)) { fgets(progress_str, MAX_STR_LEN, fin); progress_str[MAX_STR_LEN]='\0'; log_it(debug, progress_str); if (strstr(progress_str, "erifying fileset #0")) { prev_percentage = 0; regulars = true; } if (strstr(progress_str, "erifying all bigfiles")) { prev_percentage = 0; regulars = false; biggies = true; } if (strlen(progress_str)<2) { continue; } if (!strstr(progress_str, "TASK")) { continue; } progress_str[strlen(progress_str)-1] = '\0'; if (!biggies && !regulars) { strcpy(progress_str, "Still working..."); } if (strcmp(progress_str, old_progstr)) { strcpy(old_progstr, progress_str); if (biggies) { sprintf(tmp, "Large files: %s", progress_str+6); } else if (regulars) { sprintf(tmp, "Regular files: %s", progress_str+6); } if (!(p=strstr(progress_str, "% done"))) { continue; } log_it(info, tmp); if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } continue; while(!isdigit(*(p-1))) { p--; } strcpy(tmp, p); *(strchr(tmp, '%')) = '\0'; i = atoi(tmp); if (i > prev_percentage) { prev_percentage = i; log_it(info, tmp); // FIXME - could simply call send_progress_rpt_to_server() if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } } } } log_it(debug, "track_compare_task_progress() --- leaving"); pthread_exit(NULL); } /*-----------------------------------------------------------*/ void *track_restore_task_progress(void*inp) { char fname[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1], progress_str[MAX_STR_LEN+1], old_progstr[MAX_STR_LEN+1], *p; // struct s_client2server_msg_record rec; FILE*fin; bool biggies=false, regulars=false; int prev_percentage=0, i; strcpy(fname, (char*)inp); log_it(debug, "track_restore_task_progress() --- entering"); if (!(fin = fopen(fname, "r"))) { log_it(fatal, "Unable to openin tempdev while restoring"); } old_progstr[0]='\0'; sprintf(tmp, "fname = %s", fname); log_it(debug, tmp); sleep(2); if (!does_file_exist(fname)) { log_it(fatal, "track_restore_task_progress() -- fname not found"); } log_it(debug, "Working"); if (send_progress_rpt_to_server(g_server_name, "Working")) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } // FIXME --- mondorestore does not yet spit out its progress in // a user-friendly format; so, most of the following code won't // work. The server will know we're working but it won't know // how far along we've gotten. /* report on regular+biggie files */ while(!feof(fin)) { fgets(progress_str, MAX_STR_LEN, fin); progress_str[MAX_STR_LEN]='\0'; continue; log_it(debug, progress_str); if (strstr(progress_str, "estoring fileset #0")) { prev_percentage = 0; regulars = true; } if (strstr(progress_str, "erifying all bigfiles")) { prev_percentage = 0; regulars = false; biggies = true; } if (strlen(progress_str)<2) { continue; } if (!strstr(progress_str, "TASK")) { continue; } progress_str[strlen(progress_str)-1] = '\0'; if (!biggies && !regulars) { strcpy(progress_str, "Still working..."); } if (strcmp(progress_str, old_progstr)) { strcpy(old_progstr, progress_str); if (biggies) { sprintf(tmp, "Large files: %s", progress_str+6); } else if (regulars) { sprintf(tmp, "Regular files: %s", progress_str+6); } if (!(p=strstr(progress_str, "% done"))) { continue; } log_it(info, tmp); if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } continue; while(!isdigit(*(p-1))) { p--; } strcpy(tmp, p); *(strchr(tmp, '%')) = '\0'; i = atoi(tmp); if (i > prev_percentage) { prev_percentage = i; log_it(info, tmp); // FIXME - could simply call send_progress_rpt_to_server() if (send_progress_rpt_to_server(g_server_name, tmp)) { log_it(error, "Failed to send progress_rpt msg to server."); pthread_exit(NULL); } } } } log_it(debug, "track_restore_task_progress() --- leaving"); pthread_exit(NULL); } /*-----------------------------------------------------------*/ void *watch_port_for_triggers_from_server(void*arg) /* Purpose:Watch client port for incoming trigger Params: none; uses global vars instead Return: none */ { struct pollfd ufds; struct s_server2client_msg_record incoming_rec; int len, res, new_s; char tmp[MAX_STR_LEN+1]; strcpy(tmp,(char*)arg); log_it(info, "Awaiting triggers from server"); for(;;) { /* wait for ack/pong/feedback/trigger */ ufds.fd = g_sClient; ufds.events = POLLIN|POLLPRI; poll(&ufds, 1, 1000); if (!ufds.revents) { continue; } /* wait again */ len = accept_and_recv_thru_client_port(g_sClient, &new_s, &g_sinClient, (char*)&incoming_rec, sizeof(incoming_rec)); if (len<0) { log_it(error, "Unable to receive incoming trigger from server"); continue; } switch(incoming_rec.msg_type) { case trigger_backup: res = back_my_smitch_up(incoming_rec.body, new_s); /* no need to multitask: it's the client! :) */ break; case trigger_compare: res = compare_archives(incoming_rec.body, new_s); break; case trigger_restore: res = restore_archives(incoming_rec.body, incoming_rec.bodyAux, new_s); break; case logout_ok: if (!g_logging_out) { log_it(fatal, "Server has forcibly logged you out. Has server shut down?"); } g_logged_in_currently = false; g_logging_out = false; // exit(0); pthread_exit("Thou hast been logged out."); case login_fail: log_it(fatal, "Failed to login. Server thinks we're logged in already."); default: sprintf(tmp, "Received %s - '%s'", tmsg_to_string(incoming_rec.msg_type), incoming_rec.body); log_it(info, tmp); } close(new_s); } } /*-----------------------------------------------------------*/ int main(int argc, char*argv[]) /* Purpose: main subroutine Parameters: none Return: result (0=success, nonzero=failure) */ { int client_port; char hostname[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1]; // char msg[MAX_STR_LEN+1]; // bool done; // pthread_t thread; parse_options(argc, argv); log_it(info, "---------- Monitas (client) by Hugo Rabson ----------"); register_pid(getpid(), "client"); set_signals(true); srandom(time(NULL)); /* FIXME - add Ctrl-C / sigterm trapping */ /* call_program_in_background(&thread, "ls aaa"); if (program_still_running("ls aaa")) { printf("Program is still running\n"); } else { printf("Program is no longer running\n"); } exit(0); */ if (argc == 2) { strncpy(g_server_name, argv[1], MAX_STR_LEN); g_server_name[MAX_STR_LEN]='\0'; } else { fprintf(stderr, "client \n"); exit(1); } gethostname(hostname, sizeof(hostname)); sprintf(tmp, "Logging onto server as client '%s'", hostname); log_it(info, tmp); client_port = login_to_server(hostname, g_server_name); if (client_port <= 0) { fprintf(stderr, "Unable to login to server.\n"); log_it(error, "Unable to login to server."); exit(1); } // set_param_in_rcfile(g->client_rcfile, "mondoarchive_params", "-1 -L"); log_it(debug, "Awaiting commands from FIFO"); create_and_watch_fifo_for_commands(g->client_comdev); logout_and_exit(g_server_name); exit(0); } /* end main() */