Implemented `dispense -c`, bound SIGTERM to clean exist, re-created the pidfile
[tpg/opendispense2.git] / src / server / main.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * main.c - Initialisation Code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <signal.h>
14 #include "common.h"
15 #include <termios.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <stdarg.h>
20 #include <syslog.h>
21 #include <pthread.h>
22 #include "../cokebank.h"
23
24 // === IMPORTS ===
25 extern void     Init_Handlers(void);
26 extern void     Load_Itemlist(void);
27 extern void     Server_Start(void);
28 extern int      giServer_Port;
29 extern char     *gsItemListFile;
30 extern char     *gsCoke_SerialPort;
31 extern char     *gsSnack_SerialPort;
32 extern char     *gsDoor_Password;
33
34 // === PROTOTYPES ===
35 void    *Periodic_Thread(void *Unused);
36
37 // === GLOBALS ===
38  int    giDebugLevel = 0;
39 char    *gsCokebankPath = "cokebank.db";
40 // - Functions called every 20s (or so)
41 #define ciMaxPeriodics  10
42 struct sPeriodicCall {
43         void    (*Function)(void);
44 }       gaPeriodicCalls[ciMaxPeriodics];
45
46 // === CODE ===
47 void sigint_handler()
48 {
49         exit(0);
50 }
51
52 void PrintUsage(const char *progname)
53 {
54         fprintf(stderr, "Usage: %s\n", progname);
55         fprintf(stderr, "  -p    Set server port (default 11020)\n");
56         fprintf(stderr, "  -d    Set debug level (0 - 2, default 0)\n");
57         fprintf(stderr, "  --itemsfile\n");
58         fprintf(stderr, "        Set debug level (0 - 2, default 0)\n");
59         fprintf(stderr, "  --cokeport\n");
60         fprintf(stderr, "        Coke machine serial port (Default \"/dev/ttyS0\")\n");
61         fprintf(stderr, "  --doorpass\n");
62         fprintf(stderr, "        Door LAT password file (Default empty password)\n");
63         fprintf(stderr, "  --cokebank\n");
64         fprintf(stderr, "        Coke bank database file (Default \"cokebank.db\")\n");
65 }
66
67 int main(int argc, char *argv[])
68 {
69          int    i;
70         pthread_t       timer_thread;
71         
72         // Parse Arguments
73         for( i = 1; i < argc; i++ )
74         {
75                 char    *arg = argv[i];
76                 if( arg[0] == '-' && arg[1] != '-')
77                 {
78                         switch(arg[1])
79                         {
80                         case 'p':
81                                 if( i + 1 >= argc )     return -1;
82                                 giServer_Port = atoi(argv[++i]);
83                                 break;
84                         case 'd':
85                                 if( i + 1 >= argc )     return -1;
86                                 giDebugLevel = atoi(argv[++i]);
87                                 break;
88                         default:
89                                 // Usage Error?
90                                 PrintUsage(argv[0]);
91                                 return -1;
92                         }
93                 }
94                 else if( arg[0] == '-' && arg[1] == '-' ) {
95                         if( strcmp(arg, "--itemsfile") == 0 ) {
96                                 if( i + 1 >= argc )     return -1;
97                                 gsItemListFile = argv[++i];
98                         }
99                         else if( strcmp(arg, "--cokeport") == 0 ) {
100                                 if( i + 1 >= argc )     return -1;
101                                 gsCoke_SerialPort = argv[++i];
102                         }
103                         else if( strcmp(arg, "--snackport") == 0 ) {
104                                 if( i + 1 >= argc )     return -1;
105                                 gsSnack_SerialPort = argv[++i];
106                         }
107                         else if( strcmp(arg, "--doorpass") == 0 ) {
108                                 FILE    *fp;
109                                 char    buf[30];
110                                 if( i + 1 >= argc )     return -1;
111                                 fp = fopen(argv[++i], "r");
112                                 if( !fp ) {
113                                         fprintf(stderr, "ERROR: Unable to read password file\n");
114                                         perror("reading LAT password");
115                                         return -1;
116                                 }
117                                 fgets(buf, sizeof buf, fp);
118                                 fclose(fp);
119                                 gsDoor_Password = strdup(buf);;
120                         }
121                         else if( strcmp(arg, "--cokebank") == 0 ) {
122                                 if( i + 1 >= argc )     return -1;
123                                 gsCokebankPath = argv[++i];
124                         }
125                         else {
126                                 // Usage error?
127                                 PrintUsage(argv[0]);
128                                 return -1;
129                         }
130                 }
131                 else {
132                         // Usage Error?
133                         PrintUsage(argv[0]);
134                         return -1;
135                 }
136         }
137         
138         signal(SIGINT, sigint_handler);
139         signal(SIGTERM, sigint_handler);
140         
141         openlog("odispense2", 0, LOG_LOCAL4);
142         
143         if( Bank_Initialise(gsCokebankPath) )
144                 return -1;
145
146         Init_Handlers();
147
148         Load_Itemlist();
149         
150         pthread_create( &timer_thread, NULL, Periodic_Thread, NULL );
151         
152         Server_Start();
153         
154         pthread_kill(timer_thread, SIGKILL);
155
156         return 0;
157 }
158
159 void *Periodic_Thread(void *Unused)
160 {
161          int    i;
162         Unused = NULL;  // quiet, gcc
163         
164         for( ;; )
165         {
166                 sleep(10);      // Sleep for a while
167 //              printf("Periodic firing\n");
168                 for( i = 0; i < ciMaxPeriodics; i ++ )
169                 {
170                         if( gaPeriodicCalls[i].Function )
171                                 gaPeriodicCalls[i].Function();
172                 }
173         }
174         return NULL;
175 }
176
177 void AddPeriodicFunction(void (*Fcn)(void))
178 {
179         int i;
180         for( i = 0; i < ciMaxPeriodics; i ++ )
181         {
182                 if( gaPeriodicCalls[i].Function )       continue;
183                 gaPeriodicCalls[i].Function = Fcn;
184                 return ;
185         }
186         
187         fprintf(stderr, "Error: No space for %p in periodic list\n", Fcn);
188 }
189
190 int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage)
191 {
192          int    ret;
193         
194         ret = regexec(regex, string, nMatches, matches, 0);
195         if( ret == REG_NOMATCH ) {
196                 return -1;
197         }
198         if( ret ) {
199                 size_t  len = regerror(ret, regex, NULL, 0);
200                 char    errorStr[len];
201                 regerror(ret, regex, errorStr, len);
202                 printf("string = '%s'\n", string);
203                 fprintf(stderr, "%s\n%s", errorMessage, errorStr);
204                 exit(-1);
205         }
206         
207         return ret;
208 }
209
210 void CompileRegex(regex_t *regex, const char *pattern, int flags)
211 {
212          int    ret = regcomp(regex, pattern, flags);
213         if( ret ) {
214                 size_t  len = regerror(ret, regex, NULL, 0);
215                 char    errorStr[len];
216                 regerror(ret, regex, errorStr, len);
217                 fprintf(stderr, "Regex compilation failed - %s\n", errorStr);
218                 exit(-1);
219         }
220 }
221
222 // Serial helper
223 int InitSerial(const char *File, int BaudRate)
224 {
225         struct termios  info;
226          int    baud;
227          int    fd;
228         
229         fd = open(File, O_RDWR | O_NOCTTY | O_NONBLOCK);
230         if( fd == -1 )  return -1;
231         
232         switch(BaudRate)
233         {
234         case 9600:      baud = B9600;   break;
235         default:        close(fd);      return -1;
236         }
237         
238         info.c_lflag = 0;       // Non-Canoical, No Echo
239         info.c_cflag = baud | CS8 | CLOCAL | CREAD;     // baud, 8N1
240         cfsetspeed(&info, baud);
241         info.c_cc[VTIME] = 0;   // No time limit
242         info.c_cc[VMIN] = 1;    // Block until 1 char
243         
244         tcflush(fd, TCIFLUSH);
245         tcsetattr(fd, TCSANOW, &info);
246         
247         return fd;
248 }
249
250
251 /**
252  * \brief Create a formatted heap string
253  */
254 char *mkstr(const char *Format, ...)
255 {
256         va_list args;
257          int    len;
258         char    *ret;
259
260         va_start(args, Format);
261         len = vsnprintf(NULL, 0, Format, args);
262         va_end(args);
263
264         ret = malloc( len + 1 );
265         if(!ret)        return NULL;
266
267         va_start(args, Format);
268         vsprintf(ret, Format, args);
269         va_end(args);
270         
271         return ret;
272 }
273

UCC git Repository :: git.ucc.asn.au