Пример #1
0
int
main(int ac, char *av[])
{
        int          status = 0;
        char*        logfname = 0;
        /// Data directory, conffile paths may be relative
        const char*  datadir;
        int          interval = DEFAULT_INTERVAL;
        prod_spec    spec;
        prod_class_t clss;
        int          toffset = TOFFSET_NONE;
        int          loggingToStdErr = 0;
        unsigned     queue_size = 5000;
        const char*  progname = basename(av[0]);
        unsigned     logopts = LOG_CONS|LOG_PID;

        /*
         * Setup default logging before anything else.
         */
        (void)log_init(progname);

        const char*  pqfname = getQueuePath();

        spec.feedtype = DEFAULT_FEEDTYPE;
        spec.pattern = DEFAULT_PATTERN;

        if(set_timestamp(&clss.from)) /* corrected by toffset below */
        {
                int errnum = errno;
                log_error("Couldn't set timestamp: %s", strerror(errnum));
                exit(EXIT_FAILURE);
                /*NOTREACHED*/
        }
        clss.to = TS_ENDT;
        clss.psa.psa_len = 1;
        clss.psa.psa_val = &spec;

        /*
         * deal with the command line, set options
         */
        {
            extern int optind;
            extern int opterr;
            extern char *optarg;

            int ch;
            int fterr;

            opterr = 1;

            while ((ch = getopt(ac, av, "vxel:d:f:q:o:p:i:t:")) != EOF) {
                switch (ch) {
                case 'v':
                        if (!log_is_enabled_info)
                            (void)log_set_level(LOG_LEVEL_INFO);
                        break;
                case 'x':
                        (void)log_set_level(LOG_LEVEL_DEBUG);
                        break;
                case 'e':
                        key = ftok("/etc/rc.d/rc.local",'R');
                        semkey = ftok("/etc/rc.d/rc.local",'e');
                        shmid = shmget(key, sizeof(edex_message) * queue_size,
                                0666 | IPC_CREAT);
                        semid = semget(semkey, 2, 0666 | IPC_CREAT);
                        break;
                case 'l':
                        logfname = optarg;
                        (void)log_set_destination(logfname);
                        break;
                case 'd':
                        setPqactDataDirPath(optarg);
                        break;
                case 'f':
                        fterr = strfeedtypet(optarg, &spec.feedtype);
                        if(fterr != FEEDTYPE_OK)
                        {
                                log_error("Bad feedtype \"%s\", %s\n",
                                        optarg, strfeederr(fterr));
                                usage(progname);
                        }
                        break;
                case 'q':
                        pqfname = optarg;
                        break;
                case 'o':
                        toffset = atoi(optarg);
                        if(toffset == 0 && *optarg != '0')
                        {
                                log_error("invalid offset %s\n", optarg);
                                usage(progname);   
                        }
                        break;
                case 'i':
                        interval = atoi(optarg);
                        if(interval == 0 && *optarg != '0')
                        {
                                log_error("invalid interval %s\n", optarg);
                                usage(progname);   
                        }
                        break;
                case 't':
                        pipe_timeo = atoi(optarg);
                        if(pipe_timeo == 0 && *optarg != 0)
                        {
                                log_error("invalid pipe_timeo %s", optarg);
                                usage(progname);   
                        }
                        break;
                case 'p':
                        spec.pattern = optarg;
                        break;
                default:
                        usage(progname);
                        break;
                }
            }

            conffilename = getPqactConfigPath();
            datadir = getPqactDataDirPath();

            {
                int numOperands = ac - optind;

                if (1 < numOperands) {
                    log_error("Too many operands");
                    usage(progname);
                }
                else if (1 == numOperands) {
                    conffilename = av[optind];
                }
            }
        }

        setQueuePath(pqfname);
        log_notice("Starting Up");

        if ('/' != conffilename[0]) {
            /*
             * The pathname of the configuration-file is relative. Convert it
             * to absolute so that it can be (re)read even if the current
             * working directory changes.
             */
#ifdef PATH_MAX
            char    buf[PATH_MAX];          /* includes NUL */
#else
            char    buf[_POSIX_PATH_MAX];   /* includes NUL */
#endif
            if (getcwd(buf, sizeof(buf)) == NULL) {
                log_syserr("Couldn't get current working directory");
                exit(EXIT_FAILURE);
            }
            (void)strncat(buf, "/", sizeof(buf)-strlen(buf)-1);
            (void)strncat(buf, conffilename, sizeof(buf)-strlen(buf)-1);
            conffilename = strdup(buf);
            if (conffilename == NULL) {
                log_syserr("Couldn't duplicate string \"%s\"", buf);
                exit(EXIT_FAILURE);
            }
        }

        /*
         * Initialize the previous-state module for this process.
         */
        if (stateInit(conffilename) < 0) {
            log_error("Couldn't initialize previous-state module");
            exit(EXIT_FAILURE);
            /*NOTREACHED*/
        }

        /*
         * Configure the standard I/O streams for execution of child processes.
         */
        if (configure_stdio_file_descriptors()) {
            log_error("Couldn't configure standard I/O streams for execution "
                    "of child processes");
            exit(EXIT_FAILURE);
        }

        /*
         * Inform the "filel" module about the number of available file
         * descriptors.  File descriptors are reserved for stdin, stdout,
         * stderr, the product-queue, the configuration-file, and (possibly) 
         * logging.
         */
        if (0 != set_avail_fd_count(openMax() - 6))
        {
            log_error("Couldn't set number of available file-descriptors");
            log_notice("Exiting");
            exit(EXIT_FAILURE);
            /*NOTREACHED*/
        }

        /*
         * Inform the "filel" module of the shared memory segment
         */
        if (shmid != -1 && semid != -1)
        {
            set_shared_space(shmid, semid, queue_size);
        }

        /*
         * Compile the pattern.
         */
        if (re_isPathological(spec.pattern))
        {
                log_error("Adjusting pathological regular-expression: \"%s\"",
                    spec.pattern);
                re_vetSpec(spec.pattern);
        }
        status = regcomp(&spec.rgx, spec.pattern, REG_EXTENDED|REG_NOSUB);
        if(status != 0)
        {
                log_error("Can't compile regular expression \"%s\"",
                        spec.pattern);
                log_notice("Exiting");
                exit(EXIT_FAILURE);
                /*NOTREACHED*/
        }

        /*
         * register exit handler
         */
        if(atexit(cleanup) != 0)
        {
                log_syserr("atexit");
                log_notice("Exiting");
                exit(EXIT_FAILURE);
                /*NOTREACHED*/
        }

        /*
         * set up signal handlers
         */
        set_sigactions();

        /*
         * Read in (compile) the configuration file.  We do this first so
         * its syntax may be checked without opening a product queue.
         */
        if ((status = readPatFile(conffilename)) < 0) {
                exit(EXIT_FAILURE);
                /*NOTREACHED*/
        }
        else if (status == 0) {
            log_notice("Configuration-file \"%s\" has no entries. "
                "You should probably not start this program instead.",
                conffilename);
        }

        /*
         * Open the product queue
         */
        status = pq_open(pqfname, PQ_READONLY, &pq);
        if(status)
        {
                if (PQ_CORRUPT == status) {
                    log_error("The product-queue \"%s\" is inconsistent\n",
                            pqfname);
                }
                else {
                    log_error("pq_open failed: %s: %s\n",
                            pqfname, strerror(status));
                }
                exit(EXIT_FAILURE);
                /*NOTREACHED*/
        }

        if(toffset != TOFFSET_NONE) {
            /*
             * Filter and queue position set by "toffset".
             */
            clss.from.tv_sec -= toffset;
            pq_cset(pq, &clss.from);
        }
        else {
            bool       startAtTailEnd = true;
            timestampt insertTime;

            clss.from = TS_ZERO;

            /*
             * Try getting the insertion-time of the last,
             * successfully-processed data-product from the previous session.
             */
            status = stateRead(&insertTime);

            if (status) {
                log_warning("Couldn't get insertion-time of last-processed "
                        "data-product from previous session");
            }
            else {
                timestampt now;
                (void)set_timestamp(&now);

                if (tvCmp(now, insertTime, <)) {
                    log_warning("Time of last-processed data-product from previous "
                            "session is in the future");
                }
                else {
                    char buf[80];
                    (void)strftime(buf, sizeof(buf), "%Y-%m-%d %T",
                        gmtime(&insertTime.tv_sec));
                    log_notice("Starting from insertion-time %s.%06lu UTC", buf,
                        (unsigned long)insertTime.tv_usec);

                    pq_cset(pq, &insertTime);
                    startAtTailEnd = false;
                }
            }

            if (startAtTailEnd) {
                log_notice("Starting at tail-end of product-queue");
                (void)pq_last(pq, &clss, NULL);
            }
        }
Пример #2
0
/*********************************************************************
 * 
 * MAIN process
 * 
 ********************************************************************/ 
int main(int argc, char* argv[])
{
  pid_t pid;
  int num_counters=0;
  int last_counter;
  int i, k;
  PIN_STATE_t pin_value;
  PIN_STATE_t active_value=LOW;
  struct timespec pulse_start_time, pulse_end_time;
  unsigned char pulse_started;
  
   
  /* Parse input parameters */
  if ((argc==1) || ((argc==2) && !strcmp(argv[1], "-h")))
  {
    printf("Usage:\n");
    printf("  pulsecountd <pin1> <div1> [<statefile1>] [<pin2> <div2> [<statefile2>] ... [<pinN> <divN> [<statefileN>]]]\n");
    printf("      pinX:       kernel Id of GPIO pin to count pulses on (use 0 for dummy pin)\n");
    printf("      divX:       divisor for pulse count values\n");
    printf("      statefileX: file containing the counter selection\n");
    printf("                  (optional, used for dual virtual counters)\n\n");
    printf("      Note: max number of counters is %d\n", MAX_COUNTERS);
    return 1;
  }
 
  memset(counter_param, 0, sizeof(counter_param));
  for (i=1, k=0; i<(argc); i++, k++)
  {
    /* Get pin Id and divisor for each counter */
    counter_param[k].pin = atoi(argv[i++]);
    counter_param[k].divisor = atof(argv[i]);
     
    /* is this the last parameter? */
    if (i<(argc-1))
    {
      /* is next parameter a string (filename)? */
      if ((*argv[i+1] < '0') || (*argv[i+1] > '9'))
      {
        /* next parameter is the optional statefile name, save it */
        strcpy(counter_param[k].statefile, argv[++i]);
      }
    }
    if (counter_param[k].pin) num_counters++;
#if 0
    printf("counter_param[%d].pin=%d\n", k, counter_param[k].pin);
    printf("counter_param[%d].divisor=%f\n", k, counter_param[k].divisor);
    printf("counter_param[%d].statefile=%s\n\n", k, counter_param[k].statefile);
#endif
  }
  last_counter = k;

  openlog("pulsecountd", LOG_PID|LOG_CONS, LOG_USER);
  syslog(LOG_DAEMON | LOG_NOTICE, "Starting Pulse counter daemon (version %s)", VERSION);
  syslog(LOG_DAEMON | LOG_NOTICE, "Using %s logic", (active_value==HIGH)?"ACTIVE_HIGH":"ACTIVE_LOW" );

  /* Install signal handler for SIGTERM and SIGINT ("CTRL C") 
   * to be used to cleanly terminate parent annd child  processes
   */
  signal(SIGTERM, doExit);
  signal(SIGINT, doExit);
  
  /* Save the parent pid (used in signal handler) */
  parentpid = getpid();

  /* Init counter struct */
  memset((void*)&counter, 0, sizeof(counter));
  
  syslog(LOG_DAEMON | LOG_NOTICE, "Creating %d counter processes", num_counters);
  
  /* Create a child process for each counter */
  for (i=0; i<last_counter; i++)
  {  
    /* Check for dummy pin */
    if (counter_param[i].pin == 0) continue;
    
    /* Create child */
    pid=fork();
    
    if (pid == -1)
    {
      syslog(LOG_DAEMON | LOG_ERR, "Error creating child process for counter %d", i+1);
      return 2;
    }
    
    if (pid != 0)
    {
      /***** We are in the parent process *****/

      /* Save child pid */
      counter_param[i].child_pid=pid;
    }
    else
    {
      /***** We are in the child process *****/
         
      syslog(LOG_DAEMON | LOG_NOTICE, "Started child process counting pulses on GPIO pin with Kernel Id %d", 
                                                                                      counter_param[i].pin);
      
      /* Init */
      if (setup(counter_param[i], &counter) != 0)
      {
        /* Setup failed, wait to be terminated */
        while (1) usleep(100000);
      }
      
      /* Wait for possibly ongoing pulse to end */   
      while (digitalRead(counter.value_fd) == active_value) usleep(100000);
      pulse_started = 0;
      
      
      /***** Main child loop *****/
      while (1)
      {
        if (waitForEdge(&pin_value, counter.value_fd) == 0)
        {
          if (pin_value == active_value)
          {
            /* Pulse started */
            if (pulse_started == 0)
            {
              clock_gettime(CLOCK_REALTIME, &pulse_start_time);
              pulse_started = 1;
            }
            else
            {
#if DEBUG
              syslog(LOG_DAEMON | LOG_DEBUG, "Warning: detected starting pulse out of sequence on pin %d",
                                              counter_param[i].pin);
#endif
            }
          }
          else
          {
            char str[20];
            
            /* Pulse ended */
            if (pulse_started == 1)
            {
              clock_gettime(CLOCK_REALTIME, &pulse_end_time);
#if DEBUG
              syslog(LOG_DAEMON | LOG_DEBUG, "Detected pulse with length %lu ms on pin %d", 
                                              time_diff_ms(pulse_end_time, pulse_start_time), counter_param[i].pin);
#endif
              pulse_started = 0;
            }
            else
            {
#if DEBUG
              syslog(LOG_DAEMON | LOG_DEBUG, "Warning: detected ending pulse out of sequence on pin %d",
                                              counter_param[i].pin);
#endif
              continue;
            }
            
            /* TODO: check pulse lenght and filter glitches */
            
            /* Check statefile for which virtual counter to increment */
            switch (stateRead(counter.state_fd))
            {
               case 1:
                  /* Increment counter 1, apply divisor and save value */
                  counter.pulse_count1++;
                  sprintf(str, "%010lu\n", (unsigned long)(counter.pulse_count1/counter.divisor));
                  pwrite(counter.export1_fd, str, strlen(str), 0);
                  break;
                  
               case 2:
                  /* Increment counter 2, apply divisor and save value */
                  counter.pulse_count2++;
                  sprintf(str, "%010lu\n", (unsigned long)(counter.pulse_count2/counter.divisor));
                  pwrite(counter.export2_fd, str, strlen(str), 0);
                  break;
                  
               default:
                  /* unknown state, do nothing */
                  ;
                  //printf("unknown value %d in statefile\n", stateRead(counter.state_fd));
            }
          }
        }
        else
        {
          /* waitForEdge failed, wait to be terminated */
          while (1) usleep(100000);
        }
      }
    }
  }
  
  /* Create child process for TCP server */
  modbus_server_pid=fork();
  if (modbus_server_pid == -1)
  {
    syslog(LOG_DAEMON | LOG_ERR, "Error creating child process for Modbus server");
    return 3;
  }
    
  if (modbus_server_pid == 0)
  {
    /***** We are in the child process *****/
    
    modbus_server_pid = getpid();
   
    /* Start Modbus TCP server loop */
    modbustcp_server(MODBUS_SLAVE_ADDRESS,  // Modbus slave address
                     read_register_handler, // Read register handler
                     NULL                   // Write register handler
                    );
  }
  else
  {
    /***** We are in the parent process *****/

    /* Wait for termination signal */
    while (cont) sleep(1);
  }

  
  /* Wait for all counter child processes to terminate */
  for (i=0; i<last_counter; i++)
  {
    /* Check for dummy pin */
    if (counter_param[i].pin == 0) continue;
    
    kill(counter_param[i].child_pid, SIGTERM);
    if (waitpid(counter_param[i].child_pid, NULL, 0) == counter_param[i].child_pid)
    {
      syslog(LOG_DAEMON | LOG_NOTICE, "Child process %d successfully terminated", counter_param[i].child_pid);
    }
    else
    {
      syslog(LOG_DAEMON | LOG_ERR, "Error terminating child process %d", counter_param[i].child_pid);
    }
  }
  
  /* Terminate Modbus server */
  kill(modbus_server_pid, SIGTERM);
  if (waitpid(modbus_server_pid, NULL, 0) == modbus_server_pid)
  {
    syslog(LOG_DAEMON | LOG_NOTICE, "Modbus server process %d successfully terminated", modbus_server_pid);
  }
  else
  {
    syslog(LOG_DAEMON | LOG_ERR, "Error terminating Modbus server process %d", modbus_server_pid);
  }
  
  syslog(LOG_DAEMON | LOG_NOTICE, "Exiting Pulse counter daemon");
  closelog();
   
  return 0;
}