Project

General

Profile

Revision cc7646f9

IDcc7646f9d14d13acbf68e7ae50ec2e645885c4d7
Parent 5e03b78d
Child 4f824e14

Added by Thomas Mullins almost 11 years ago

Major changes to mainbox code.

-Added local cache for RFID -> tool permissions
-Queries new page /crm/roboauth/%08x, which gives bitmask of all tool
permissions instead of just one
-Added pid file at /var/run/tooltron.pid
-Now must be run as "tooltron run" and also has "tooltron refresh" and
"tooltron clear" which send signals to the pid in the pid file
-Other things I'm forgetting

View differences:

mainbox/Makefile
1
SRC=main.c tool.c query.c event.c util.c log.c
2
HDR=tool.h query.h event.h util.h log.h ../tooltron_mb.h
1
SRC=main.c tool.c query.c event.c util.c log.c cache.c
2
HDR=tool.h query.h event.h util.h log.h cache.h ../tooltron_mb.h
3 3

  
4 4
FLAGS=-O2 -g -Wall -I.. `pkg-config --cflags --libs libmodbus libcurl`
5 5

  
mainbox/cache.c
1
#include "cache.h"
2
#include "log.h"
3
#include <stdlib.h>
4

  
5
struct entry_t {
6
  unsigned int key;
7
  unsigned int value;
8
  time_t retrieved;
9
  struct entry_t *next;
10
};
11

  
12
struct entry_t *cache[CACHE_SIZE];
13

  
14
void cache_foreach(cache_func f) {
15
  struct entry_t *entry;
16
  int i;
17

  
18
  for (i = 0; i < CACHE_SIZE; i++) {
19
    for (entry = cache[i]; entry; entry = entry->next)
20
      f(entry->key);
21
  }
22
}
23

  
24
void cache_clear() {
25
  struct entry_t *entry, *next;
26
  int i;
27

  
28
  for (i = 0; i < CACHE_SIZE; i++) {
29
    for (entry = cache[i]; entry; entry = next) {
30
      next = entry->next;
31
      free(entry);
32
    }
33
    cache[i] = NULL;
34
  }
35
}
36

  
37
struct entry_t *cache_find(unsigned int key) {
38
  struct entry_t *entry;
39

  
40
  for (entry = cache[key % CACHE_SIZE]; entry; entry = entry->next) {
41
    if (entry->key == key)
42
      return entry;
43
  }
44

  
45
  return NULL;
46
}
47

  
48
void cache_add(unsigned int key, unsigned int value) {
49
  struct entry_t *entry;
50
  int i;
51

  
52
  entry = malloc(sizeof(struct entry_t));
53
  if (!entry) {
54
    log_print("ERROR: Out of memory; clearing cache");
55
    cache_clear();
56
    return;
57
  }
58

  
59
  i = key % CACHE_SIZE;
60
  entry->key = key;
61
  entry->value = value;
62
  entry->next = cache[i];
63
  cache[i] = entry;
64
}
65

  
66
int cache_lookup(unsigned int key, unsigned int *value) {
67
  struct entry_t *entry;
68
  
69
  entry = cache_find(key);
70
  if (entry) {
71
    *value = entry->value;
72
    return 1;
73
  } else
74
    return 0;
75
}
76

  
77
void cache_update(unsigned int key, unsigned int value) {
78
  struct entry_t *entry;
79

  
80
  entry = cache_find(key);
81
  if (entry)
82
    entry->value = value;
83
  else
84
    cache_add(key, value);
85
}
mainbox/cache.h
1
#ifndef CACHE_H
2
#define CACHE_H
3

  
4
#define CACHE_SIZE 256
5

  
6
typedef void (*cache_func)(unsigned int key);
7

  
8
void cache_foreach(cache_func f);
9
void cache_clear();
10
int cache_lookup(unsigned int key, unsigned int *value);
11
void cache_update(unsigned int key, unsigned int value);
12

  
13
#endif
mainbox/log.c
24 24

  
25 25
void log_perror(const char *s) {
26 26
  log_time();
27
  perror(s);
27
  printf("%s: %s\n", s, sys_errlist[errno]);
28 28
}
mainbox/main.c
1 1
#include "tool.h"
2 2
#include "query.h"
3
#include "cache.h"
3 4
#include "log.h"
5
#include "util.h"
4 6
#include <unistd.h>
5 7
#include <signal.h>
6
#include <strings.h>
8
#include <string.h>
7 9
#include <stdio.h>
8 10

  
9
#define SLEEP_MS 10//250
11
#define SLEEP_MS 25
10 12

  
11 13
static struct tool_t tools[] = {
12
  TOOL_DECL("test1", 1),
13
  TOOL_DECL("test2", 2),
14
  TOOL_DECL("test3", 3),
15
  TOOL_DECL("test4", 4),
16
  TOOL_DECL("test5", 5),
17
  TOOL_DECL("test6", 6),
18
  TOOL_DECL("test7", 7),
19
  TOOL_DECL("test8", 8),
20
  TOOL_DECL("test9", 9)
14
  TOOL_DECL("Mill", 1),
15
  TOOL_DECL("Lathe", 2),
16
  TOOL_DECL("Drill Press", 3),
17
  TOOL_DECL("Drill Press", 4),
18
  TOOL_DECL("CNC", 5),
19
  TOOL_DECL("Bandsaw", 6),
20
  TOOL_DECL("Circular Saw", 7),
21
  TOOL_DECL("Wood Band Saw", 8),
22
  TOOL_DECL("Chop/Miter Saw", 9),
23
  TOOL_DECL("Belt Sander", 10)
21 24
};
22 25

  
23 26
#define N_TOOLS (sizeof(tools)/sizeof(struct tool_t))
24 27

  
25 28
volatile int run = 1;
29
volatile int refresh_cache = 0;
30
volatile int clear_cache = 0;
26 31

  
27 32
void sigint(int sig) {
28 33
  run = 0;
29 34
}
30 35

  
31
void print_usage(const char *name) {
32
  printf("Usage: %s [-h] [-d serial_device] [-s db_server[:port]]\n", name);
33
  printf("       -h prints this message\n");
34
  printf("       -d specifies the serial port for Modbus\n");
35
  printf("          defaults to /dev/ttyUSB0\n");
36
  printf("       -s specifies the server where the CRM is running\n");
37
  printf("          defaults to roboticsclub.org\n");
36
void sigusr1(int sig) {
37
  refresh_cache = 1;
38 38
}
39 39

  
40
int main(int argc, char **argv) {
41
  int i, opt;
42
  struct sigaction sigact;
43
  const char *device = "/dev/ttyUSB0";
44
  const char *server = "roboticsclub.org";
40
void sigusr2(int sig) {
41
  clear_cache = 1;
42
}
45 43

  
46
  while ((opt = getopt(argc, argv, "hd:s:")) != -1) {
47
    switch (opt) {
48
      case 'h':
49
        print_usage(argv[0]);
50
        return 0;
51
      case 'd':
52
        device = optarg;
53
        break;
54
      case 's':
55
        server = optarg;
56
        break;
57
      default:
58
        print_usage(argv[0]);
59
        return 1;
60
    }
61
  }
44
void send_signal(int sig) {
45
  pid_t pid;
46

  
47
  pid = read_pid_file();
48

  
49
  if (pid > 0)
50
    kill(pid, sig);
51
}
52

  
53
int tooltron_main(const char *device, const char *server) {
54
  struct sigaction sigact;
55
  int i;
62 56

  
63 57
  log_print("Serial device: %s", device);
64 58
  log_print("CRM server: http://%s/", server);
65 59

  
66 60
  bzero(&sigact, sizeof(sigact));
67
  sigact.sa_handler = sigint;
68 61
  sigact.sa_flags = SA_RESTART;
69 62
  sigemptyset(&sigact.sa_mask);
63

  
64
  sigact.sa_handler = sigint;
70 65
  sigaction(SIGINT, &sigact, NULL);
66
  sigaction(SIGTERM, &sigact, NULL);
67

  
68
  sigact.sa_handler = sigusr1;
69
  sigaction(SIGUSR1, &sigact, NULL);
70

  
71
  sigact.sa_handler = sigusr2;
72
  sigaction(SIGUSR2, &sigact, NULL);
71 73

  
72 74
  if (query_init(server)) {
73 75
    return 1;
......
83 85
  while (run) {
84 86
    tool_poll(&tools[i]);
85 87
    event_q_process();
88
    if (refresh_cache) {
89
      log_print("Recieved SIGUSR1, refreshing cache content");
90
      query_refresh_cache();
91
      refresh_cache = 0;
92
    }
93
    if (clear_cache) {
94
      log_print("Recieved SIGUSR2, clearing cache");
95
      cache_clear();
96
      clear_cache = 0;
97
    }
86 98
    usleep(SLEEP_MS * (useconds_t)1000);
87 99
    i = (i+1) % N_TOOLS;
88 100
  }
......
96 108
  tool_close_mb();
97 109

  
98 110
  log_print("Exiting");
111
  query_cleanup();
112
  cache_clear();
99 113
  return 0;
100 114
}
115

  
116
char *usage =
117
"Usage: %s [-h] [-d device] [-s server[:port]] <cmd>\n"
118
"       -h prints this message\n"
119
"       -d specifies the serial port for Modbus\n"
120
"          defaults to /dev/ttyUSB0\n"
121
"       -s specifies the server where the CRM is running\n"
122
"          defaults to roboticsclub.org\n"
123
"       <cmd> can be any of the following:\n"
124
"          run     runs tooltron if it is not already running\n"
125
"          refresh signals an already running tooltron to refresh its cache\n"
126
"          clear   signals an already running tooltron to clear its cache\n";
127

  
128
int main(int argc, char **argv) {
129
  int opt;
130
  const char *device = "/dev/ttyUSB0";
131
  const char *server = "roboticsclub.org";
132

  
133
  while ((opt = getopt(argc, argv, "hd:s:")) != -1) {
134
    switch (opt) {
135
      case 'h':
136
        /* Print usage, not an error */
137
        printf(usage, argv[0]);
138
        return 0;
139
      case 'd':
140
        device = optarg;
141
        break;
142
      case 's':
143
        server = optarg;
144
        break;
145
      default:
146
        /* Unknown option, error */
147
        printf(usage, argv[0]);
148
        return 1;
149
    }
150
  }
151

  
152
  if (optind >= argc) {
153
    /* Not enough arguments, error */
154
    printf(usage, argv[0]);
155
    return 1;
156
  }
157

  
158
  if (strcmp(argv[optind], "refresh") == 0) {
159
    send_signal(SIGUSR1);
160
    return 0;
161
  } else if (strcmp(argv[optind], "clear") == 0) {
162
    send_signal(SIGUSR2);
163
    return 0;
164
  } else if (strcmp(argv[optind], "run") != 0) {
165
    /* <cmd> is not "refresh", "clear", or "run", error */
166
    printf(usage, argv[0]);
167
    return 1;
168
  }
169

  
170
  if (create_pid_file())
171
    /* pid file already exists, error */
172
    return 1;
173

  
174
  return tooltron_main(device, server);
175
}
mainbox/query.c
1 1
#include "query.h"
2
#include "cache.h"
2 3
#include "event.h"
3 4
#include "util.h"
4 5
#include "log.h"
......
10 11
/* Outputs the response to /add_card_event to debug.html */
11 12
//#define DEBUG_EVENT_RESPONSE
12 13

  
13
const char *server;
14
char *tooltron_password;
14
/* Size of buffer written to by write_buffer(). We are only reading integers
15
 * back from the server, so we don't need much of the response and anything
16
 * else can be ignored */
17
#define WRITE_BUFFER_SIZE 30
18

  
19
static const char *server;
20
static char *tooltron_password;
21
static char buffer[WRITE_BUFFER_SIZE];
22
static int buffer_idx;
15 23

  
16 24
int query_init(const char *server_name) {
17 25
  CURLcode error_code;
......
41 49
    free(tooltron_password);
42 50
}
43 51

  
44
static size_t write_bool(void *buffer, size_t size, size_t nmemb, void *userp) {
45
  int *resultp = userp;
46
  char *str = buffer;
52
static size_t write_buffer(void *buf_in, size_t size, size_t nmemb, void *userp) {
53
  size_t to_read;
47 54

  
48
  if (size*nmemb > 0 && str[0] == '1')
49
    *resultp = 1;
50
  else
51
    *resultp = 0;
55
  to_read = nmemb;
52 56

  
53
  return nmemb;
57
  if (buffer_idx + to_read*size > WRITE_BUFFER_SIZE)
58
    to_read = (WRITE_BUFFER_SIZE - buffer_idx)/size;
59

  
60
  if (to_read > 0) {
61
    memcpy(buffer+buffer_idx, buf_in, to_read*size);
62
    buffer_idx += to_read*size;
63
  }
64

  
65
  return to_read;
54 66
}
55 67

  
56 68
static size_t write_ignore(void *buffer, size_t size, size_t nmemb,
......
59 71
}
60 72

  
61 73
/*
62
 * query_user_permission
74
 * do_q_user_perm
63 75
 *
64
 * Makes an HTTP request to the CRM server to see if user_id has access to
65
 * tool_id. Returns 1 if the server replies with '1' or 0 otherwise.
76
 * Makes an HTTP request to the CRM server to see what tools user_id has access
77
 * to. Returns a bitmask, and returns 0 if there was a problem.
66 78
 */
67
int query_user_permission(int tool_id, unsigned int user_id) {
79
unsigned int do_q_user_perm(unsigned int user_id) {
68 80
  CURL* handle;
69 81
  CURLcode error_code;
70 82
  char url[1024];
......
75 87
  if (handle == NULL)
76 88
    return 0;
77 89

  
78
  sprintf(url, "http://%s/crm/roboauth/%08x/%d/", server, user_id, tool_id);
90
  sprintf(url, "http://%s/crm/roboauth/%08x/", server, user_id);
79 91
  error_code = curl_easy_setopt(handle, CURLOPT_URL, url);
80 92
  if (error_code) goto error;
81 93

  
82
  error_code = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_bool);
94
  buffer_idx = 0;
95
  error_code = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_buffer);
83 96
  if (error_code) goto error;
84 97

  
85
  error_code = curl_easy_setopt(handle, CURLOPT_WRITEDATA, &result);
98
  error_code = curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
86 99
  if (error_code) goto error;
87 100

  
88 101
  error_code = curl_easy_perform(handle);
......
95 108
  else if (response > 200)
96 109
    log_print("WARNING: response %ld from %s", response, url);
97 110

  
111
  result = atoi(buffer);
112
  cache_update(user_id, result);
113

  
98 114
  curl_easy_cleanup(handle);
99 115
  return result;
100 116

  
101 117
error:
102 118
  log_print("ERROR: curl: %s", curl_easy_strerror(error_code));
103
  log_print("ERROR:       when authenticating user %08x on tool %d",
104
      user_id, tool_id);
119
  log_print("ERROR:       when authenticating user %08x", user_id);
105 120
  curl_easy_cleanup(handle);
106 121
  return 0;
107 122
}
108 123

  
124
void do_refresh(unsigned int key) {
125
  do_q_user_perm(key);
126
}
127

  
128
/*
129
 * query_refresh_cache
130
 *
131
 * Queries the CRM server to update every user permission entry in the cache.
132
 */
133
void query_refresh_cache() {
134
  cache_foreach(do_refresh);
135
}
136

  
137
/*
138
 * query_user_permission
139
 *
140
 * Checks whether user_id has permission for tool_id. First checks the cache,
141
 * then if that fails, calls do_q_user_perm to make an HTTP request to the CRM
142
 * server.
143
 */
144
int query_user_permission(int tool_id, unsigned int user_id) {
145
  unsigned int result;
146

  
147
  if (cache_lookup(user_id, &result))
148
    log_print("Serving permissions for %08x from cache", user_id);
149
  else {
150
    log_print("Requesting permissions for %08x from server", user_id);
151
    result = do_q_user_perm(user_id);
152
  }
153

  
154
  return (result >> tool_id) & 1;
155
}
156

  
109 157
/*
110 158
 * query_add_event
111 159
 *
......
125 173
  struct tm *timeinfo;
126 174
  long response = 0;
127 175

  
128
  return 0;
129

  
130 176
#ifdef DEBUG_EVENT_RESPONSE
131 177
  FILE *fdebug;
132 178
  fdebug = fopen("debug.html", "w");
mainbox/query.h
6 6
int query_init();
7 7
void query_cleanup();
8 8
int query_user_permission(int tool_id, unsigned int user_id);
9
void query_refresh_cache();
9 10
int query_add_event(struct event_t *event);
10 11

  
11 12
#endif
mainbox/tool.c
99 99
}
100 100

  
101 101
void tool_request_disable(struct tool_t *tool) {
102
  log_print("Requesting disable on %s (%d)", tool->name, tool->address);
103
  tool_write_coil(MB_COIL_REQ_DIS, 1);
104
  tool->state = TS_REQ_DIS;
102
  if (tool->state == TS_ON) {
103
    log_print("Requesting disable on %s (%d)", tool->name, tool->address);
104
    tool_write_coil(MB_COIL_REQ_DIS, 1);
105
    tool->state = TS_REQ_DIS;
106
  }
105 107
}
106 108

  
107 109
void tool_poll(struct tool_t *tool) {
mainbox/util.c
2 2
#include "log.h"
3 3
#include <stdlib.h>
4 4
#include <stdio.h>
5
#include <errno.h>
5 6
#include <unistd.h>
6 7
#include <sys/types.h>
7 8
#include <sys/stat.h>
8 9
#include <fcntl.h>
9 10

  
11
/*
12
 * read_file
13
 *
14
 * Reads in an entire file. Returns NULL on error, or a malloc'd pointer to a
15
 * string which should later be freed.
16
 */
10 17
char *read_file(const char *filename) {
11 18
  int fd, len, size, nread;
12 19
  char *str;
......
46 53
    }
47 54
  }
48 55
}
56

  
57
/*
58
 * create_pid_file
59
 *
60
 * Creates /var/run/tooltron.pid containing the PID of the current process.
61
 * Returns 0 if successful, or nonzero if there is an error or it already
62
 * exists.
63
 */
64
int create_pid_file() {
65
  int fd;
66
  FILE *file;
67
  
68
  fd = open("/var/run/tooltron.pid", O_CREAT | O_EXCL | O_WRONLY, 0644);
69
  if (fd < 0) {
70
    if (errno == EEXIST)
71
      fprintf(stderr, "ERROR: tooltron is already running, or the pidfile "
72
          "/var/run/tooltron.pid is stale");
73
    else
74
      log_perror("open");
75
    return 1;
76
  }
77

  
78
  file = fdopen(fd, "w");
79
  if (!file) {
80
    log_perror("fdopen");
81
    return 1;
82
  }
83

  
84
  fprintf(file, "%d", getpid());
85

  
86
  fclose(file);
87
  return 0;
88
}
89

  
90
/*
91
 * remove_pid_file
92
 *
93
 * Removes /var/run/tooltron.pid.
94
 */
95
void remove_pid_file() {
96
  if (unlink("/var/run/tooltron.pid"))
97
    log_perror("unlink");
98
}
99

  
100
/*
101
 * read_pid_file
102
 *
103
 * Returns the integer found in /var/run/tooltron.pid, or 0 if there was an
104
 * error.
105
 */
106
pid_t read_pid_file() {
107
  FILE *file;
108
  int pid;
109

  
110
  file = fopen("/var/run/tooltron.pid", "r");
111
  if (!file) {
112
    if (errno == ENOENT)
113
      fprintf(stderr, "ERROR: tooltron does not appear to be running\n");
114
    else
115
      perror("fopen");
116
    return 0;
117
  }
118

  
119
  if (fscanf(file, "%d", &pid) != 1) {
120
    perror("fscanf");
121
    fclose(file);
122
    return 0;
123
  }
124

  
125
  fclose(file);
126
  return pid;
127
}
mainbox/util.h
1 1
#ifndef UTIL_H
2 2
#define UTIL_H
3 3

  
4
/* Reads in an entire file. Returns NULL on error, or a malloc'd pointer to a
5
 * string which should later be freed. */
4
#include <unistd.h>
5

  
6 6
char *read_file(const char *filename);
7
int create_pid_file();
8
pid_t read_pid_file();
9
void remove_pid_file();
7 10

  
8 11
#endif

Also available in: Unified diff