/* * hwapi_backd.c * * Copyright (c) 2009 Ismael Gomez-Miguelez, UPC , * Xavier Reves, UPC * All rights reserved. * * * This file is part of ALOE. * * ALOE is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ALOE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALOE. If not, see . * */ /** @brief Hardware API Background Daemon for Linux * * This file contains the implementation of the HWAPI * Background Daemon for Linux. * * This program creats all resources that will be used by * ALOE Software Daemons as well as acts as a bridge * towards external network interfaces. */ #define _GNU_SOURCE #define _SVID_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mem.h" #include "phal_hw_api.h" #include "hwapi_backd.h" #include "hwapi_netd.h" #include "params.h" #include "parse_str.h" //#define DEB void print_frame(char *title); void check_commands(); void sig_chld(int sig, siginfo_t * sinfl, void *v); void sig_usr(int sig, siginfo_t * sinfl, void *v); void sig_int(int sig, siginfo_t * sinfl, void *v); void clear_files(); void clear_zombies(); void clear_shm(); void remove_file(int i); char Answer_command(int cmd, char *packet, int len); void launch_daemons(); void kill_chlds(); void clear_zombies(); void remove_all_msg(); int exit_level = 0; key_t shm_key; int shm_id, mem_sem_id, status_sem_id; struct hw_api_db *hw_api_db = NULL; struct net_db *net_db; int api_msg_key, data_msg_key; int data_msg_id, api_msg_id; struct msgbuff msg; char filelockname[DEF_FILE_LEN]; pid_t runph_pid; int die_silent = 1; /** @defgroup launcher ALOE Hardware API Background Daemon for Linux * * This program creats all resources that will be used by * ALOE Software Daemons as well as acts as a bridge * towards external network interfaces. * * Communication with Daemons is done through two interfaces: * - Shared Memory: Common definitions, running processes, shared * interfaces, etc. * - Commands (messages): requesting for some services (see Answer_command()) like creating * a new process, starting an external interface, etc. * * @{ */ /** Exit Function * * Function called at exit to clear resources */ #define ELEVEL_PROCESSES 3 #define ELEVEL_MEM 2 #define ELEVEL_LOCK 1 int creating_daemon = 0; int verbose=0; #define printv(a) \ if (verbose) printf(a) void exit_runph() { int sleep_time; if (getpid() != runph_pid) { return; } sleep_time = 500; die_silent = 0; if (exit_level >= ELEVEL_PROCESSES) { printv("Killing chlds...\n"); kill_chlds(sleep_time); clear_zombies(); clear_files(); } if (exit_level >= ELEVEL_MEM) { if (hw_api_db->dac.ptr) { close_dac(); } printv("cleaning shared memory resources...\n"); clear_shm(); } if (exit_level >= ELEVEL_LOCK && !creating_daemon) { printv("Removing lock file\n"); if (strlen(filelockname) > 0) { remove_all_msg(); if (remove(filelockname)) perror("remove"); } fflush(NULL); } } int gettimerres(void) { struct timespec ts; if (clock_getres(CLOCK_MONOTONIC, &ts)) return 1; return ts.tv_nsec; } /************* * USAGE * *************/ void usage(char *prog) { printf( "%s -r [repository_path] -c [platform_cfg_file] -i [xitf_cfg_file] --debug -v [--daemon] [-o output_log]\n", prog); printf( "\t -r [repository_path]: Path for file resources (for manager daemons)\n"); printf("\t -c [platform_config_file]: Platform configuration file\n"); printf("\t -i [xitf_cfg_file]: External interface configuration file\n"); printf("\t -f Force deletetion of previous session\n"); printf( "\t --daemon: Run as a daemon. You should select log file for each daemon in platform config file.\n"); printf( "\t -o [output_log]: Redirect hwapi stdout to a file, useful when runnin as daemon.\n"); printf( "\t -l [lock_file]: Set another lock file name to enable virtual platforms.\n"); printf("\t -t [num_processors]: Run MAC capacity test and quit.\n"); printf("\t --debug: Run in debug mode.\n"); printf("\t -v: Run in verbose mode.\n"); exit(0); } int sems[100]; typedef union semun { int val; struct semid_ds *buf; ushort * array; } semun_t; void run_mactest(int procs, int nops) { int i, j, x, n; struct sched_param param; time_t tdata[3], tdata2[3], t; semun_t arg; for (i = 1; i < procs; i++) { sems[i] = semget(IPC_PRIVATE, 1, O_CREAT | 0666); if (sems[i] == -1) { perror("Error creating semaphore"); exit(-1); } arg.val = 0; if (semctl(sems[i], 0, SETVAL, arg) < 0) { perror("semctl failure"); return 0; } } if (getuid()) { printf("This test needs root privileges\n"); exit(0); } mlockall(MCL_CURRENT | MCL_FUTURE); param.__sched_priority = SCHED_PRIO; if (sched_setscheduler(0, SCHED_TYPE, ¶m)) { printf("HWAPI_BACKD: Error setting scheduler\n"); perror("setscheduler"); } printf("Running Integer MAC test for %d procs %d ops\n", procs, nops); gettimeofday(&tdata2[1], NULL); for (i = 0; i < procs; i++) { n = fork(); if (n == -1) { printf("Error forking.\n"); perror("fork"); return; } if (!n) { gettimeofday(&t, NULL); sleep_us(1000000 - t.tv_usec); x = 1; gettimeofday(&tdata[1], NULL); for (j = 0; j < nops; j++) { x += 33 * x; } gettimeofday(&tdata[2], NULL); get_time_interval(tdata); printf("child %d: %d:%d\t-> %d:%d\tdiff: %d:%d\t%d MMACS\n", i, tdata[1].tv_sec, tdata[1].tv_usec, tdata[2].tv_sec, tdata[2].tv_usec, tdata[0].tv_sec, tdata[0].tv_usec, nops / tdata[0].tv_usec); exit(0); } } while (wait(&x) > 0) ; gettimeofday(&tdata2[2], NULL); get_time_interval(tdata2); printf("parent: %d:%d->%d:%d diff: %d:%d\n", tdata2[1].tv_sec, tdata2[1].tv_usec, tdata2[2].tv_sec, tdata2[2].tv_usec, tdata2[0].tv_sec, tdata2[0].tv_usec); } void clear_shm() { int i, j; /* if shared memory is created */ if (shmget(shm_key, sizeof(struct hw_api_db), 0) != -1) { for (i = 0; i < MAX_INT_ITF; i++) { if (hw_api_db->int_itf[i].id) { if (msgctl(hw_api_db->int_itf[i].id, IPC_RMID, NULL) == -1) { printf("error removing msg itf id %d\n", hw_api_db->int_itf[i].id); perror("msgctl"); } } } for (j = 0; j < MAX_CORES; j++) { if (hw_api_db->semids[j]) { if (semctl(hw_api_db->semids[j], 0, IPC_RMID) == -1) { perror("semctl"); } } } /* unattach pointer if exists */ if (hw_api_db) { shmdt(hw_api_db); } /* and clear shared memory */ shmctl(shmget(shm_key, sizeof(struct hw_api_db), 0), IPC_RMID, NULL); } /* same for msg for api */ if (msgget(api_msg_key, 0) != -1) { if (msgctl(msgget(api_msg_key, 0), IPC_RMID, NULL)) { printf("error removing msg itf id %d\n", hw_api_db->int_itf[i].id); perror("msgctl"); } } if (msgget(data_msg_key, 0) != -1) { if (msgctl(msgget(data_msg_key, 0), IPC_RMID, NULL)) { printf("error removing msg itf id %d\n", hw_api_db->int_itf[i].id); perror("msgctl"); } } if (mem_sem_id) { if (semctl(mem_sem_id, 0, IPC_RMID) == -1) { perror("semctl"); } } if (status_sem_id) { if (semctl(status_sem_id, 0, IPC_RMID) == -1) { perror("semctl"); } } } void create_shm() { struct msqid_ds info; printv("Creating main shared memory\n"); if ((shm_id = shmget(shm_key, sizeof(struct hw_api_db), IPC_CREAT | IPC_EXCL | 0666)) == -1) { fprintf(stderr, "HWAPI_BACKD: Shared memory could not be created\n"); perror("shmget"); exit(-1); } if ((hw_api_db = (struct hw_api_db *) shmat(shm_id, NULL, 0)) == (struct hw_api_db *) -1) { fprintf(stderr, "Shared memory could not be attached\n"); perror("shmat"); exit(-1); } printv("Creating mailbox\n"); /* Create daemons' api communications mbox */ if ((api_msg_id = msgget(api_msg_key, IPC_CREAT | IPC_EXCL | 0666)) == -1) { fprintf(stderr, "HWAPI_BACKD: Message queue for deamons control could not be created\n"); perror("msgget"); exit(-1); } /* Create daemons data communications mbox */ if ((data_msg_id = msgget(data_msg_key, IPC_CREAT | IPC_EXCL | 0666)) == -1) { fprintf(stderr, "HWAPI_BACKD: Message queue for data could not be created\n"); perror("msgget"); exit(-1); } msgctl(data_msg_id, IPC_STAT, &info); info.msg_qbytes = MSG_QBYTES; if (msgctl(data_msg_id, IPC_SET, &info) == -1) { perror("msgctl"); printf( "HWAPI_BACKD: Error setting size of message queue. Configure kernel to allow msg buffers of %d bytes.\n\n" "\t1. Set '/proc/sys/kernel/msgmnb' to '%d'. It will be reset on next system boot.\n" "\t2. Add line: 'kernel.msgmnb=%d' to '/etc/sysctl.conf' and run 'sysctl -p' as root\n", MSG_QBYTES, MSG_QBYTES, MSG_QBYTES); exit(-1); } struct msginfo info2; msgctl(data_msg_id, IPC_INFO, (struct msqid_ds*) &info2); if (info2.msgmni < MAX_INT_ITF) { printf( "HWAPI_BACKD: Maximum number of message queues must be bigger than %d.\n\n" "\t1. Set '/proc/sys/kernel/msgmni' to '%d'. It will be reset on next system boot.\n" "\t2. Add line: 'kernel.msgmni=%d' to '/etc/sysctl.conf' and run 'sysctl -p' as root\n", MAX_INT_ITF, MAX_INT_ITF, MAX_INT_ITF); exit(-1); } if (info2.msgmax < MSG_BUFF_SZ) { printf( "HWAPI_BACKD: Maximum size for messages must be bigger than %d.\n\n" "\t1. Set '/proc/sys/kernel/msgmax' to '%d'. It will be reset on next system boot.\n" "\t2. Add line: 'kernel.msgmax=%d' to '/etc/sysctl.conf' and run 'sysctl -p' as root\n", MSG_BUFF_SZ, MSG_BUFF_SZ, MSG_BUFF_SZ); exit(-1); } msgctl(data_msg_id, IPC_STAT, &info); #ifdef DEB_MEM printf("HWAPI_BACKD: Msg queue for data comms created with id %d, size %d\n", data_msg_id, (int) info.msg_qbytes); #endif mem_sem_id = semget(IPC_PRIVATE, 1, O_CREAT | 0666); if (mem_sem_id == -1) { perror("Error creating semaphore"); exit(-1); } status_sem_id = semget(IPC_PRIVATE, 1, O_CREAT | 0666); if (status_sem_id == -1) { perror("Error creating semaphore"); exit(-1); } } /** Init Shared Memory * * Initializes shared memory regions */ void init_shm_db() { int i, j; semun_t arg; printv("Initiating shared memory\n"); /* create memory pool object for statistics variables */ /* default slot size to 32 bytes */ if (!mem_new(hw_api_db->var_table, VAR_TABLE_SZ, 32, mem_sem_id)) { printf("HWAPI_BACKD: Error creating shared memory!\n"); exit(-1); } get_time(&hw_api_db->init_time); #ifdef DISABLE_LOGS hw_api_db->cpu_info.disable_logs = 1; #endif hw_api_db->int_itf[0].key_id = HW_API_MSG_KEY; hw_api_db->int_itf[1].key_id = DATA_MSG_KEY; hw_api_db->status_sem_id = status_sem_id; printv("Creating semaphores\n"); /** Create semaphores */ for (j = 0; j < hw_api_db->cpu_info.nof_cores; j++) { hw_api_db->semids[j] = semget(IPC_PRIVATE, MAX_PROCS, O_CREAT | 0666); if (hw_api_db->semids[j] == -1) { perror("Error creating semaphore"); exit(-1); } for (i = 1; i < MAX_PROCS; i++) { arg.val = 0; if (semctl(hw_api_db->semids[j], i, SETVAL, arg) < 0) { perror("semctl failure"); return 0; } } } hw_api_db->semdaemons = semget(IPC_PRIVATE, MAX_DAEMONS, O_CREAT | 0666); if (hw_api_db->semdaemons == -1) { perror("Error creating semaphore"); exit(-1); } for (i = 0; i < MAX_DAEMONS; i++) { hw_api_db->daemons[i].semid = i; arg.val = 0; if (semctl(hw_api_db->semdaemons, i, SETVAL, arg) < 0) { perror("semctl failure"); return 0; } } } void run_daemon() { int i, status; struct rlimit resourceLimit = { 0 }; printv("Forking daemon\n"); switch (fork()) { case -1: perror("fork"); exit(0); case 0: break; default: printv("Exiting parent\n"); exit(0); } printv("Getting rlimit\n"); resourceLimit.rlim_max = 0; status = getrlimit(RLIMIT_NOFILE, &resourceLimit); if (-1 == status) { /* shouldn't happen */ perror("getrlimit"); exit(1); } if (0 == resourceLimit.rlim_max) { printf("Max number of open file descriptors is 0!!\n"); exit(1); } status = setsid(); if (-1 == status) { perror("setsid"); exit(1); } printv("Closing file descriptors\n"); for (i = 0; i < 3; i++) { close(i); } open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); #ifdef kk switch (fork()) { case -1: perror("fork"); exit(0); case 0: break; default: exit(0); } umask(0002); int fileDesc = open("/dev/null", O_RDWR); /* stdin */ dup(fileDesc); /* stdout */ dup(fileDesc); /* stderr */ #endif } void remove_all_msg() { key_t key; int i; for (i = 0; i < 0x100; i++) { key = ftok(filelockname, i); if (msgget(key, 0) != -1) { msgctl(msgget(key, 0), IPC_RMID, NULL); } } } char cl[128]; int clean_lock(char *lock_file_name) { int i, fd, n; int pid; key_t key; sprintf(filelockname, "%s/%s.%s", LOCK_PATH, LOCK_PREFIX, lock_file_name); printf("Cleaning resources associated to lock: %s\n", filelockname); fd = open(filelockname, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU); if (fd > 0) { close(fd); } else { if (!(n = fork())) { execl("/usr/local/bin/killph", "/usr/local/bin/killph", NULL); perror("execl"); exit(0); } /*else { waitpid(n,NULL,0); }*/ } printf("Cleaning shared memory...\n"); key = ftok(filelockname, HW_API_SHM_KEY); if (shmget(key, sizeof(struct hw_api_db), 0) != -1) { printf("Removed ok.\n"); shmctl(shmget(key, sizeof(struct hw_api_db), 0), IPC_RMID, NULL); } /* remove all messages associated to this file */ printf("Cleaning messages...\n"); remove_all_msg(); printf("Removing File...\n"); if (remove(filelockname)) { if (errno != ENOENT) { perror("remove"); return 0; } } else { printf("Ok.\n"); } return 1; } int lock_file(char *lock_file_name) { int fd; sprintf(filelockname, "%s/%s.%s", LOCK_PATH, LOCK_PREFIX, lock_file_name); fd = open(filelockname, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU); if (fd < 0) { if (errno == EEXIST) { printf( "Another ALOE session is currently opened with lock %s. \n" "Kill it or set another lock at platform configuration file or launch with -f flag to delete previous session.\n", filelockname); return 0; } else { printf("Error opening %s\n", filelockname); perror("open_lock"); return 0; } } close(fd); printf("HWAPI_BACKD: Created lock file %s\n", filelockname); return 1; } int set_lock_pid(int pid) { FILE *f; f = fopen(filelockname, "w"); if (!f) { printf("Error opening lock file\n"); return 0; } fprintf(f, "%d", pid); fclose(f); return 1; } int output_fd; /** Main Daemon Function * * This is the main function for the Daemon. It creates shared memory resources * reads some configuration files (for interfaces) and launches network childs. */ int main(int argc, char **argv) { struct stat st; struct hw_api_db *tmp_db; struct sigaction action; int i, n; char *daemons_file, *xitf_file; int report_file_fd = 0; int force_clean = 0; float ts_sec; exit_level = -1; printf("\n\nFIXME: Es queda un proces al fer killph quan esta en daemon\n\n"); n = gettimerres(); if (n > 1) { printf( "\nRUNPHD: WARNING: High resolution timers not available.\nCurrent resolution=%d\n\n", n); } /** Register exit function */ atexit(exit_runph); runph_pid = getpid(); /* set default parameters */ daemons_file = DEFAULT_DAEMONS_FILE; xitf_file = DEFAULT_XITF_FILE; memset(filelockname, 0, DEF_FILE_LEN); /* check if daemons or xitf file path is overriden by command options */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'v': verbose=1; break; case 'h': usage(argv[0]); exit(0); case 'c': if ((argc - 1) > i) { daemons_file = argv[i + 1]; i++; } else { printf("Switch '-c' needs a config file name.\n"); usage(argv[0]); } break; case 'i': if ((argc - 1) > i) { xitf_file = argv[i + 1]; i++; } else { printf("Switch '-I' needs a config file name.\n"); usage(argv[0]); } break; case 't': if ((argc - 2) > i) { run_mactest(atoi(argv[i + 1]), atoi(argv[i + 2])); exit(0); } else { printf("Switch '-t' needs number of processors\n"); usage(argv[0]); } break; case 'g': execlp("./aloeui.run", (char*) 0); perror("execl"); exit(0); case 'f': force_clean = 1; break; } } } printv("Allocating temporal memory\n"); tmp_db = calloc(1, sizeof(struct hw_api_db)); if (!tmp_db) { perror("malloc"); exit(0); } /* try to get lock now (unless virtual platforms is enabled in the configuration file) */ if (tmp_db->lock_file[0] == '\0') { strncpy(tmp_db->lock_file, LOCK_FILE, DEF_FILE_LEN); } if (force_clean) { if (!clean_lock(tmp_db->lock_file)) { printf("Could not clean previous lock. Shall do it manually.\n"); } exit(0); } printv("Locking file\n"); if (!lock_file(tmp_db->lock_file)) { printf("Error getting lock. Exiting...\n"); exit(0); } exit_level=ELEVEL_LOCK; /* parse these files */ FILE *f; f = fopen(daemons_file, "r"); if (f == NULL) { printf("HWAPI_BACKD: Error while opening config file %s\n", daemons_file); perror("open1"); exit(-1); } printf("HWAPI_BACKD: Parsing cpu config file..."); fflush(stdout); n = parse_cpu_cfg(f, tmp_db->daemons, MAX_DAEMONS, tmp_db); fclose(f); if (n < 0) { printf("HWAPI_BACKD: There was an error parsing file %s (%d)\n", daemons_file, n); exit(-1); } else { printf("ok %d daemons\n", n); } if (!tmp_db->cpu_info.intBW) { tmp_db->cpu_info.intBW = DEFAULT_INTERNAL_BW; } /* Parse interface config file */ if (xitf_file) { f = fopen(xitf_file, "r"); if (!f) { printf( "HWAPI_BACKD: Error opening itf file %s. No interfaces will be configured\n", xitf_file); } else { printf("HWAPI_BACKD: Parsing interface config file..."); fflush(stdout); n = parse_itf_cfg(f, tmp_db->net_db.ext_itf, MAX_EXT_ITF); fclose(f); } if (n < 0) { printf("HWAPI_BACKD: There was an error parsing file %s (%d)\n", xitf_file, n); exit(-1); } else { printf("ok %d itf\n", n); tmp_db->net_db.nof_ext_itf = n; } } /* now read rest of parameters, wich override file ones */ for (i = 1; i < argc; i++) { switch (argv[i][1]) { case 'p': if ((argc - 1) > i) { tmp_db->do_nice = 1; tmp_db->priority = atoi(argv[i + 1]); i++; } else { printf("Switch '-p' needs a priority level.\n"); usage(argv[0]); } break; case 'r': if ((argc - 1) > i) { strncpy(tmp_db->res_path, argv[i + 1], DEF_FILE_LEN); i++; } else { printf("Switch '-r' needs a directory.\n"); usage(argv[0]); } break; case 'o': if ((argc - 1) > i) { strncpy(tmp_db->output_file, argv[i + 1], DEF_FILE_LEN); i++; } else { printf("Switch '-o' needs a report file.\n"); usage(argv[0]); } break; case '-': if (!strcmp(&argv[i][1], "-daemon")) { tmp_db->run_as_daemon = 1; } else if (!strcmp(&argv[i][1], "-no-daemon")) { tmp_db->run_as_daemon = 0; } else if (!strcmp(&argv[i][1], "-debug")) { tmp_db->cpu_info.debug_level = 1; } else { printf("Unknown argument %s\n", argv[i]); usage(argv[0]); } break; default: break; } } exit_level = ELEVEL_LOCK; if (tmp_db->res_path[0] == '\0') { printf("Missing working path.\n\n"); usage(argv[0]); exit(0); } if (stat(tmp_db->res_path, &st)) { printf("Working path %s not found\n", tmp_db->res_path); exit(0); } printf("HWAPI_BACKD: Using working path: %s\n", tmp_db->res_path); if (tmp_db->do_nice) { if (getuid() != 0) { printf("Program must be run as root to set priorities.\n"); exit(0); } printf("HWAPI_BACKD: Running objects with priority level %d\n", tmp_db->priority); } if (tmp_db->cpu_info.debug_level) { printf("HWAPI_BACKD: Running in DEBUG mode\n"); } if (tmp_db->report_file[0] != '\0') { printf("HWAPI_BACKD: Reporting hwinfo to %s\n", tmp_db->report_file); } if (tmp_db->output_file[0] != '\0' && tmp_db->run_as_daemon) { printf("HWAPI_BACKD: Redirecting output to %s\n", tmp_db->output_file); } else { printv("Output is not redirected\n"); } if (tmp_db->run_as_daemon) { printf("HWAPI_BACKD: Running as a daemon...\n"); } else { printv("Running in stand-alone mode\n"); } if (!tmp_db->cpu_info.nof_cores) { xprintf( "HWAPI_BACKD: CAUTION missing number of cores parameter. Setting to 1 core.\n"); tmp_db->cpu_info.nof_cores = 1; } if (!tmp_db->cpu_info.C) { xprintf( "HWAPI_BACKD: CAUTION no CPU info was entered in daemons cfg. Setting default %d\n", DEFAULT_CPU_MIPS); tmp_db->cpu_info.C = DEFAULT_CPU_MIPS; } else { xprintf("HWAPI_BACKD: Local Computing Capacity: %.1f MOP/s %d cores\n", tmp_db->cpu_info.C, tmp_db->cpu_info.nof_cores); xprintf("HWAPI_BACKD: Internal Bandwidth: %.1f MBps\n", tmp_db->cpu_info.intBW); } /* run in background */ if (tmp_db->run_as_daemon) { printv("Running in Background\n"); if (tmp_db->output_file[0] == '\0') { printf( "Caution you didn't select an output file and I'm going background. Output will be lost.\n"); } else { printf( "HWAPI_BACKD: Going background... Output is being redirected to %s\n", tmp_db->output_file); } creating_daemon = 1; run_daemon(); creating_daemon = 0; } else { printv("Running in foreground\n"); } if (tmp_db->output_file[0] != '\0' && tmp_db->run_as_daemon) { fclose(stdout); fclose(stderr); stdout = fopen(tmp_db->output_file, "w+"); //O_CREAT | O_TRUNC | O_RDWR, 0666); if (!stdout) { printf("Opening output log file %s\n", hw_api_db->output_file); exit(0); } stderr = stdout; if (!tmp_db->run_as_daemon) { printf("From now on, redirecting output to %s\n", tmp_db->output_file); } printf("=== Here begins hwapi output ===\n"); } /* set pid at lock file */ printv("Setting lock pid\n"); if (!set_lock_pid(getpid())) { printf("Error writting to lock file\n"); exit(0); } /* update my pid */ runph_pid = getpid(); if (tmp_db->report_file[0] != '\0') { report_file_fd = open(tmp_db->report_file, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR); if (report_file_fd < 0) { printf("Error while opening report file %s\n", tmp_db->report_file); exit(-1); } } /*Create shared memory control area */ shm_key = ftok(filelockname, HW_API_SHM_KEY); api_msg_key = ftok(filelockname, HW_API_MSG_KEY); data_msg_key = ftok(filelockname, DATA_MSG_KEY); exit_level++; clear_shm(); exit_level = ELEVEL_MEM; create_shm(); printv("Copying temporal db to hwapidb\n"); memcpy(hw_api_db, tmp_db, sizeof(struct hw_api_db)); free(tmp_db); init_shm_db(); net_db = &hw_api_db->net_db; exit_level = ELEVEL_PROCESSES; /** parsing DAC config requires hwapi to be initiated */ printv("Setting up DAC\n"); if (hw_api_db->dac.ptr != NULL) { /** find exec semaphore */ i = 0; while (i < MAX_DAEMONS && strcmp(hw_api_db->daemons[i].path, "exec")) i++; if (i == MAX_DAEMONS) { xprintf( "HWAPI_BACKD: Error configuring DAC. Can't find EXEC daemon\n"); exit(-1); } /* configure dac */ if (!configure_dac(&hw_api_db->dac, hw_api_db->semdaemons, hw_api_db->daemons[i].semid)) { printf("HWAPI_BACKD: Error configuring DAC.Exiting...\n"); exit(-1); } /* set time slot duration */ hw_api_db->cpu_info.tslen_usec = hw_api_db->dac.ts_len; } if (!hw_api_db->cpu_info.tslen_usec) { xprintf("HWAPI_BACKD: Error can't run with tslot 0\n"); exit(-1); } else { xprintf("HWAPI_BACKD: Time slot duration set to %d usec\n", hw_api_db->cpu_info.tslen_usec); } printv("Setting up signals\n"); /*Capture SIGCHLD signals */ action.sa_sigaction = sig_chld; action.sa_flags = SA_SIGINFO; sigemptyset(&action.sa_mask); if (sigaction(SIGCHLD, &action, NULL) < 0) { perror("sigaction"); exit(-1); } /* capture SIGUSR1 signal */ action.sa_sigaction = sig_usr; action.sa_flags = SA_SIGINFO; sigemptyset(&action.sa_mask); if (sigaction(SIGUSR1, &action, NULL) < 0) { perror("sigaction"); exit(-1); } /* Init network thread */ net_db->net_chld = 0; if (net_db->nof_ext_itf) { printv("Creating network threads\n"); Net_thread(hw_api_db, data_msg_id); } /* wait to be created */ sleep_ms(500); /* start daemons at boot */ printv("Launching daemons...\n"); launch_daemons(); exit_level++; /* capture SIGINT signal */ action.sa_sigaction = sig_int; action.sa_flags = SA_SIGINFO; sigemptyset(&action.sa_mask); if (sigaction(SIGINT, &action, NULL) < 0) { perror("sigaction"); exit(-1); } /** fall to IDLE */ printv("Falling to IDLE\n"); while (1) { sleep_ms(100); check_commands(); fflush(stdout); } /* type ctrl+c to exit */ return (1); } /** Died child signal capture. * * Captures signals for SIGCHLD (a child dies). */ void sig_chld(int sig, siginfo_t * sinfl, void *v) { int i, j; time_t t; /* if it is the main network child, this is a fatal error, exit program */ if (net_db->net_chld > 0) { if (waitpid(net_db->net_chld, NULL, WNOHANG) > 0) { if (die_silent) { printf( "HWAPI_BACKD: Fatal error. Main network child died. Should exit...\n"); } net_db->net_chld = 0; } } /* if it is daemon, it is also a fatal error, exit program */ for (i = 0; i < MAX_DAEMONS; i++) { if (hw_api_db->daemons[i].pid) { if (waitpid(hw_api_db->daemons[i].pid, NULL, WNOHANG) > 0) { if (die_silent) { printf( "HWAPI_BACKD: Fatal error. Daemon %s died. Should exit...\n", hw_api_db->daemons[i].path); } hw_api_db->daemons[i].pid = 0; } } } /* if it is an object, set pid to 0. Here as we don't want to exit, * we will check all childs */ for (i = 0; i < MAX_PROCS; i++) { if (hw_api_db->proc_db[i].pid) { if (waitpid(hw_api_db->proc_db[i].pid, NULL, WNOHANG) > 0) { get_time(&t); if (die_silent && hw_api_db->proc_db[i].status.cur_status != 6 && hw_api_db->proc_db[i].status.cur_status != 7) { printf( "HWAPI_BACKD: Object %s (Id: 0x%x) died at %d:%d. TS=%d Status=%d\n", hw_api_db->proc_db[i].obj_name, hw_api_db->proc_db[i].obj_id, (int) t.tv_sec, (int) t.tv_usec, hw_api_db->proc_db[i].obj_tstamp, hw_api_db->proc_db[i].status.cur_status); } remove_file(i); hw_api_db->proc_db[i].pid = 0; /* move all schedule */ for (j = 0; j < MAX_PROCS; j++) { if (hw_api_db->proc_db[j].core_idx == hw_api_db->proc_db[i].core_idx) { if (hw_api_db->proc_db[j].exec_position > hw_api_db->proc_db[i].exec_position) { if (hw_api_db->proc_db[j].exec_position == hw_api_db->proc_db[i].exec_position + 1) { post_sem_n( hw_api_db->semids[hw_api_db->proc_db[i].core_idx], hw_api_db->proc_db[i].exec_position + 1); } hw_api_db->proc_db[j].exec_position--; } } } } } } /* finnally, check all network childs */ for (i = 0; i < net_db->nof_ext_itf; i++) { if (net_db->ext_itf[i].pid > 0) { if (waitpid(net_db->ext_itf[i].pid, NULL, WNOHANG) > 0) { if (die_silent) { printf("HWAPI_BACKD: Network child pid %d has died.\n", net_db->ext_itf[i].pid); } net_db->ext_itf[i].pid = -1; if (net_db->ext_itf[i].mode & FLOW_WRITE_ONLY) { net_db->ext_itf[i].out_mbox_id = 0; } } } if (net_db->ext_itf[i].bpid > 0) { if (waitpid(net_db->ext_itf[i].bpid, NULL, WNOHANG) > 0) { if (die_silent) { printf( "HWAPI_BACKD: Network bidirectional child pid %d has died.\n", sinfl->si_pid); } net_db->ext_itf[i].bpid = 0; net_db->ext_itf[i].out_mbox_id = 0; } } } } void sig_int(int sig, siginfo_t * sinfl, void *v) { exit_level = 10; exit_runph(); exit_level = -1; exit(0); } void check_commands() { int n; int ret; n = msgrcv(api_msg_id, &msg, MSG_SZ, MSG_MAGIC, IPC_NOWAIT); if (n > 0) { ret = Answer_command(msg.body[0], &msg.body[1], n - MSG_HEAD_SZ - 2); /* answer with return value */ msg.head.dst = msg.head.src; msg.body[0] = ret; msgsnd(api_msg_id, &msg, n, 0); } else if (n == -1) { if (errno != ENOMSG) { perror("Receiving message from child"); exit(-1); } else { return; } } } /** Message signal capture (command from a daemon). * */ void sig_usr(int sig, siginfo_t * sinfl, void *v) { check_commands(); } int nprocs = 0; /** Command Processing. * * @todo Relative path when launching received processes * * Accepts the following commands with the following contents. First word is the * command type, following is data needed by this command: * - HWD_SETUP_XITF: Starts an external interface (configured at interface configuration file). * - word 1: Id of the external interface. * - word 2: Mailbox of the internal interface to be bridged. * - word 3: Mode (READ_ONLY/WRITE_ONLY). * - CREATE_PROCESS: Creates a process. * - word 0: Position in the process database of the process to be * created. */ char Answer_command(int cmd, char *packet, int len) { int i, id; int pid; key_t itf_key; struct sched_param param; semun_t arg; switch (cmd) { case HWD_SETUP_XITF: i = 0; while (i < net_db->nof_ext_itf && net_db->ext_itf[i].id != (hwitf_t) packet[0]) { i++; } if (i == net_db->nof_ext_itf) { printf("HWAPI_BACKD: Itf id %d does not exist.\n", packet[0]); return -1; } itf_key = ftok(filelockname, (int) packet[1]); if ((id = msgget(itf_key, 0)) == -1) { xprintf("HWAPI_BACKD: Error attaching to itf %d\n", packet[1]); perror("msgget"); return -1; } if (packet[2] == FLOW_READ_ONLY) { net_db->ext_itf[i].in_mbox_id = id; net_db->ext_itf[i].in_key_id = (hwitf_t) packet[1]; } else if (packet[2] == FLOW_WRITE_ONLY) { net_db->ext_itf[i].out_mbox_id = id; net_db->ext_itf[i].out_key_id = (hwitf_t) packet[1]; } else { printf("HWAPI_BACKD: Invalid mode 0x%x while setting itf\n", packet[2]); return -1; } int j = 0; while (j < MAX_INT_ITF && hw_api_db->int_itf[j].key_id != (hwitf_t) packet[1]) j++; if (j < MAX_INT_ITF) { hw_api_db->int_itf[j].network_pid = net_db->ext_itf[i].pid; hw_api_db->int_itf[j].network_bpid = net_db->ext_itf[i].bpid; } return 1; break; case HWD_CREATE_PROCESS: /* process index is received in the packet */ i = packet[0]; nprocs++; /* create child */ switch (pid = fork()) { case -1: perror("fork"); return -1; case 0: mlockall(MCL_CURRENT | MCL_FUTURE); if (!getuid()) { param.__sched_priority = SCHED_PRIO; if (sched_setscheduler(0, SCHED_TYPE, ¶m)) { printf("HWAPI_BACKD: Error setting scheduler\n"); perror("setscheduler"); } /* if (setpriority(PRIO_PROCESS,0,-20)) { printf("HWAPI_BACKD: Error setting priority\n"); perror("setpriority"); } */} /* set pid, this indicates a successfull creation of the child */ hw_api_db->proc_db[i].pid = getpid(); execl(hw_api_db->proc_db[i].path, hw_api_db->proc_db[i].path, NULL); printf("HWAPI_BACKD: Error while launching process %s\n", hw_api_db->proc_db[i].path); exit(-1); default: /* parent returns child pid */ memcpy(packet, &pid, 4); return 1; } break; default: printf("HWAPI_BACKD: Error unknown command 0x%x\n", cmd); break; } printf( "HWAPI_BACKD: Error answering command... Not suposed to arrive here.\n"); return -1; } #define MAX_ARGS 20 /** Start Daemons * * Starts all daemons (parsed from the daemon configuration file and saved in daemons structure) * by forking and exec() */ void launch_daemons() { int i; int pid; int argc; char *argv[MAX_ARGS]; struct sched_param param; cpu_set_t set; char *x; for (i = 0; i < MAX_DAEMONS; i++) { if (hw_api_db->daemons[i].path[0] != '\0') { if (hwapi_hwinfo_isDebugging()) { x = strdup(hw_api_db->daemons[i].path); if (strstr(hw_api_db->daemons[i].path, "cmdman")) { sprintf(hw_api_db->daemons[i].path, "cmdman_console/bin/%s", x); } else { sprintf(hw_api_db->daemons[i].path, "sw_daemons/lnx_make/bin/%s", x); } free(x); } if (strstr(hw_api_db->daemons[i].path, "cmdman")) { if (!strstr(hw_api_db->daemons[i].path, "-l") && !strstr( hw_api_db->daemons[i].path, "-d")) { if (hw_api_db->run_as_daemon) { sprintf(hw_api_db->daemons[i].path, "%s -l -d", hw_api_db->daemons[i].path); } } } argc = string_to_argv(hw_api_db->daemons[i].path, argv, MAX_ARGS); if (argc < 1) { printf("HWAPI_BACKD: Invalid program command: %s\n", hw_api_db->daemons[i].path); exit(-1); } /* create child */ switch (pid = fork()) { case -1: perror("fork"); exit(-1); case 0: mlockall(MCL_CURRENT | MCL_FUTURE); /* set processor affinity */ CPU_ZERO(&set); CPU_SET(0, &set); if (sched_setaffinity(pid, sizeof(cpu_set_t), &set)) { perror("sched_setaffinity"); } if (!getuid()) { if (strstr(hw_api_db->daemons[i].path, "exec")) { hw_api_db->exec_pid = getpid(); param.__sched_priority = EXEC_PRIO; if (sched_setscheduler(0, SCHED_TYPE, ¶m)) { printf("HWAPI_BACKD: Error setting scheduler\n"); perror("setscheduler"); } } else if (strstr(hw_api_db->daemons[i].path, "bridge")) { param.__sched_priority = BRIDGE_PRIO; if (sched_setscheduler(0, SCHED_TYPE, ¶m)) { printf("HWAPI_BACKD: Error setting scheduler\n"); perror("setscheduler"); } } else if (strstr(hw_api_db->daemons[i].path, "sync")) { param.__sched_priority = SYNC_PRIO; if (sched_setscheduler(0, SCHED_TYPE, ¶m)) { printf("HWAPI_BACKD: Error setting scheduler\n"); perror("setscheduler"); } } } /* save pid */ hw_api_db->daemons[i].pid = getpid(); /* launch process */ execvp(argv[0], argv); printf("\nHWAPI_BACKD: Error launching %s\n\n", hw_api_db->daemons[i].path); perror("execvp"); hw_api_db->daemons[i].pid = 0; exit(-1); default: break; } } } } int kill_child(int *pid, int sleep_time, char *name) { int c; kill(*pid, SIGTERM); c = 0; while (*pid > 0 && !waitpid(*pid, NULL, WNOHANG)) { sleep_ms(sleep_time); c++; if (c == 10) { printf( "HWAPI_BACKD: Thread '%s' with pid %d not responding. Sending kill...\n", name, *pid); kill(*pid, SIGKILL); return 1; } } return 0; } /** Close Daemons * * Terminates all daemons and proccesses by sending a SIGTERM */ void kill_chlds(int wait_time) { int i; int sleep_time; int error = 0; if (!wait_time) { wait_time = 500; } sleep_time = wait_time / 10; /* kill all phal processes */ for (i = 0; i < MAX_PROCS; i++) { if (hw_api_db->proc_db[i].pid > 0) { error += kill_child(&hw_api_db->proc_db[i].pid, sleep_time, hw_api_db->proc_db[i].obj_name); } } /* and daemons */ for (i = 0; i < MAX_DAEMONS; i++) { if (hw_api_db->daemons[i].pid > 0) { error += kill_child(&hw_api_db->daemons[i].pid, sleep_time, hw_api_db->daemons[i].path); } } /* and network childs */ if (hw_api_db->net_db.net_chld > 0) { error += kill_child(&hw_api_db->net_db.net_chld, sleep_time, "Network Main"); for (i = 0; i < hw_api_db->net_db.nof_ext_itf; i++) { if (hw_api_db->net_db.ext_itf[i].pid > 0) { error += kill_child(&hw_api_db->net_db.ext_itf[i].pid, sleep_time, "Interface"); } if (hw_api_db->net_db.ext_itf[i].bpid > 0) { error += kill_child(&hw_api_db->net_db.ext_itf[i].bpid, sleep_time, "Interface"); } } } if (error) { printf("HWAPI_BACKD: Some processes were not closed properly (%d).\n", error); } } void remove_file(int i) { int j; /* check that any other process is using the same executable */ for (j = 0; j < MAX_PROCS; j++) { if (j != i) { if (!strncmp(hw_api_db->proc_db[i].path, hw_api_db->proc_db[j].path, SYSTEM_STR_MAX_LEN)) { break; } } } if (j == MAX_PROCS) { if (remove(hw_api_db->proc_db[i].path)) { perror("remove"); } } hw_api_db->proc_db[i].path[0] = '\0'; } void clear_files() { int i; for (i = 0; i < MAX_PROCS; i++) { if (hw_api_db->proc_db[i].path[0] != '\0') { remove_file(i); } } } /** Clear all zombies (my childs) */ void clear_zombies() { while (waitpid(-1, NULL, WNOHANG) > 0) ; while (wait(NULL) > 0) ; } /* print frame function */ void print_frame(char *title) { printf("======================================\n"); printf(" -= %s =- \n", title); printf("======================================\n"); } /** @} */