aboutsummaryrefslogtreecommitdiffstats
path: root/plugin_exec.c
blob: 565ace1b5ac31e6babee4fed1754aee4685cef9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
generated by cgit v1.2.3 (git 2.25.1) at 2024-07-04 23:22:33 +0000
 


href='#n273'>273
274
275
276
277
278
279
280
281
282
283
284
/* $Id: plugin_exec.c,v 1.2 2004/04/08 10:48:25 reinelt Exp $
 *
 * plugin for external processes
 *
 * Copyright 2004 Michael Reinelt <reinelt@eunet.at>
 * Copyright 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 *
 * based on the old 'exec' client which is
 * Copyright 2001 Leopold T�tsch <lt@toetsch.at>
 *
 *
 * This file is part of LCD4Linux.
 *
 * LCD4Linux 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 2, or (at your option)
 * any later version.
 *
 * LCD4Linux 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * $Log: plugin_exec.c,v $
 * Revision 1.2  2004/04/08 10:48:25  reinelt
 * finished plugin_exec
 * modified thread handling
 * added '%x' format to qprintf (hexadecimal)
 *
 * Revision 1.1  2004/03/20 11:49:40  reinelt
 * forgot to add plugin_exec.c ...
 *
 */

/* 
 * exported functions:
 *
 * int plugin_init_exec (void)
 *  adds functions to start external pocesses
 *
 */


#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "debug.h"
#include "plugin.h"
#include "hash.h"
#include "cfg.h"
#include "thread.h"
#include "qprintf.h"


#define NUM_THREADS 16
#define SHM_SIZE 256

typedef struct {
  int   delay;
  int   mutex;
  pid_t pid;
  int   shmid;
  char *cmd;
  char *key;
  char *ret;
} EXEC_THREAD;

static EXEC_THREAD Thread[NUM_THREADS];
static int max_thread = -1;

static HASH EXEC = { 0, };


// x^0 + x^5 + x^12
#define CRCPOLY 0x8408 
  
static unsigned short CRC (unsigned char *s)
{
  int i;
  unsigned short crc;

  // seed value
  crc=0xffff;
  
  while (*s!='\0') {
    crc ^= *s++;
    for (i = 0; i < 8; i++)
      crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0);
  }
  return crc;
}


static void exec_thread (void *data)
{
  EXEC_THREAD *Thread = (EXEC_THREAD*)data;
  FILE *pipe;
  char buffer[SHM_SIZE];
  int len;
  
  // use a safe path
  putenv ("PATH=/usr/local/bin:/usr/bin:/bin");
  
  // forever...
  while (1) {
    pipe = popen(Thread->cmd, "r");
    if (pipe == NULL) {
      error("exec error: could not run pipe '%s': %s", Thread->cmd, strerror(errno));
      len = 0;
    } else {
      len = fread(buffer, 1, SHM_SIZE-1, pipe);
      if (len <= 0) {
	error("exec error: could not read from pipe '%s': %s", Thread->cmd, strerror(errno));
	len = 0;
      }
      pclose(pipe);
    }
    
    // force trailing zero
    buffer[len] = '\0';
    
    // remove trailing CR/LF
    while (len>0 && (buffer[len-1]=='\n' || buffer[len-1]=='\r')) {
      buffer[--len]='\0';
    }
    
    // lock shared memory
    mutex_lock(Thread->mutex);
    // write data
    strncpy(Thread->ret, buffer, SHM_SIZE);
    // unlock shared memory
    mutex_unlock(Thread->mutex);
    usleep (Thread->delay);
  }
}


static void destroy_exec_thread (int n)
{
  if (Thread[n].mutex != 0) mutex_destroy(Thread[n].mutex); 
  if (Thread[n].cmd) free (Thread[n].cmd);
  if (Thread[n].key) free (Thread[n].key);
  if (Thread[n].ret) shm_destroy (Thread[n].shmid, Thread[n].ret);
  
  Thread[n].delay = 0;
  Thread[n].mutex = 0;
  Thread[n].pid   = 0;
  Thread[n].shmid = 0;
  Thread[n].cmd   = NULL;
  Thread[n].key   = NULL;
  Thread[n].ret   = NULL;
}


static int create_exec_thread (char *cmd, char *key, int delay)
{
  char name[10];

  if (max_thread >= NUM_THREADS) {
    error ("cannot create exec thread <%s>: thread buffer full!", cmd);
    return -1;
  }

  max_thread++;
  Thread[max_thread].delay = delay;
  Thread[max_thread].mutex = mutex_create(); 
  Thread[max_thread].pid   = -1;
  Thread[max_thread].cmd   = strdup(cmd);
  Thread[max_thread].key   = strdup(key);
  Thread[max_thread].ret   = NULL;
    
  // create communication buffer
  Thread[max_thread].shmid = shm_create ((void**)&Thread[max_thread].ret, SHM_SIZE);

  // catch error
  if (Thread[max_thread].shmid < 0) {
    error ("cannot create exec thread <%s>: shared memory allocation failed!", cmd);
    destroy_exec_thread (max_thread--);
    return -1;
  }
  
  // create thread
  qprintf(name, sizeof(name), "exec-%s", key); 
  Thread[max_thread].pid = thread_create (name, exec_thread, &Thread[max_thread]);

  // catch error
  if (Thread[max_thread].pid < 0) {
    error ("cannot create exec thread <%s>: fork failed?!", cmd);
    destroy_exec_thread (max_thread--);
    return -1;
  }
  
  return 0;
}    


static int do_exec (char *cmd, char *key, int delay)
{
  int i, age;
  
  age = hash_age(&EXEC, key, NULL);
  
  if (age < 0) {
    hash_set (&EXEC, key, "");
    // first-time call: create thread
    if (delay < 10) {
      error ("exec(%s): delay %d is too short! using 10 msec", cmd, delay);
      delay = 10;
    }
    if (create_exec_thread (cmd, key, 1000*delay)) {
      return -1;
    }
    return 0;
  }
  
  // reread every 10 msec only
  if (age > 0 && age <= 10) return 0;
  
  // find thread
  for (i = 0; i <= max_thread; i++) {
    if (strcmp(key, Thread[i].key) == 0) {
      // lock shared memory
      mutex_lock(Thread[i].mutex);
      // copy data
      hash_set (&EXEC, key, Thread[i].ret);
      // unlock shared memory
      mutex_unlock(Thread[i].mutex);
      return 0;
    }
  }
  
  error ("internal error: could not find thread exec-%s", key);
  return -1;
}

static void my_exec (RESULT *result, RESULT *arg1, RESULT *arg2)
{
  char *cmd, key[5], *val;
  int delay;
  
  cmd = R2S(arg1);
  delay = (int)R2N(arg2);

  qprintf (key, sizeof(key), "%x", CRC(cmd));
  
  if (do_exec(cmd, key, delay) < 0) {
    SetResult(&result, R_STRING, ""); 
    return;
  }
  
  val = hash_get(&EXEC, key);
  if (val == NULL) val = "";
  
  SetResult(&result, R_STRING, val); 
}


int plugin_init_exec (void)
{
  AddFunction ("exec", 2, my_exec);
  return 0;
}


void plugin_exit_exec(void) 
{
  int i;
  
  for (i=0; i<=max_thread; i++) {
    destroy_exec_thread(i);
  }
  
  hash_destroy(&EXEC);
}