Initial move to common config code directory
[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 <stdbool.h>
13 #include <string.h>
14 #include <signal.h>
15 #include "common.h"
16 #include <termios.h>
17 #include <unistd.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <stdarg.h>
21 #include <syslog.h>
22 #include <pthread.h>
23 #include "../cokebank.h"
24 #include "../common/config.h"
25
26 // === IMPORTS ===
27 extern void     Init_Handlers(void);
28 extern void     Load_Itemlist(void);
29 extern void     Server_Start(void);
30 extern int      gbServer_RunInBackground;
31 extern int      giServer_Port;
32 extern const char       *gsItemListFile;
33 extern const char       *gsCoke_ModbusAddress;
34 extern const char       *gsDoor_SerialPort;
35 extern bool     gbSyslogEnabled;
36
37 // === PROTOTYPES ===
38 void    *Periodic_Thread(void *Unused);
39
40 // === GLOBALS ===
41  int    giDebugLevel = 0;
42  int    gbNoCostMode = 0;
43 const char      *gsCokebankPath = "cokebank.db";
44 // - Functions called every 20s (or so)
45 #define ciMaxPeriodics  10
46 struct sPeriodicCall {
47         void    (*Function)(void);
48 }       gaPeriodicCalls[ciMaxPeriodics];
49 pthread_t       gTimerThread;
50
51 // === CODE ===
52 void sigint_handler()
53 {
54         exit(0);
55 }
56
57 void PrintUsage(const char *progname)
58 {
59         fprintf(stderr, "Usage: %s\n", progname);
60         fprintf(stderr, "  -f,--configfile\n");
61         fprintf(stderr, "        Set the config file path (default `dispsrv.conf')\n");
62         fprintf(stderr, "  -d    Set debug level (0 - 2, default 0)\n");
63         fprintf(stderr, "  --[dont-]daemonise\n");
64         fprintf(stderr, "        Run (or explicitly don't run) the server disconnected from the terminal\n");
65 }
66
67 int main(int argc, char *argv[])
68 {
69          int    i;
70         const char      *config_file = "dispsrv.conf";
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 'f':
81                                 if( i + 1 >= argc )     return -1;
82                                 config_file = argv[++i];
83                                 break;
84                         case 'd':
85                                 if( i + 1 >= argc )     return -1;
86                                 Config_AddValue("debug_level", argv[++i]);
87                                 giDebugLevel = atoi(argv[i]);
88                                 break;
89                         default:
90                                 // Usage Error
91                                 fprintf(stderr, "Unknown option '-%c'\n", arg[1]);
92                                 PrintUsage(argv[0]);
93                                 return -1;
94                         }
95                 }
96                 else if( arg[0] == '-' && arg[1] == '-' )
97                 {
98                         if( strcmp(arg, "--configfile") == 0 ) {
99                                 if( i + 1 >= argc )     return -1;
100                                 config_file = argv[++i];
101                         }
102                         else if( strcmp(arg, "--daemonise") == 0 ) {
103                                 Config_AddValue("daemonise", "true");
104                         }
105                         else if( strcmp(arg, "--dont-daemonise") == 0 ) {
106                                 Config_AddValue("daemonise", "false");
107                         }
108                         else {
109                                 // Usage error
110                                 fprintf(stderr, "Unknown option '%s'\n", arg);
111                                 PrintUsage(argv[0]);
112                                 return -1;
113                         }
114                 }
115                 else
116                 {
117                         // Usage Error
118                         PrintUsage(argv[0]);
119                         return -1;
120                 }
121         }
122
123         Config_ParseFile( config_file );
124
125         // Parse config values
126         gbServer_RunInBackground = Config_GetValue_Bool("daemonise", 0);
127         gsCokebankPath       = Config_GetValue("cokebank_database", 0);
128         gsDoor_SerialPort    = Config_GetValue("door_serial_port", 0);
129         gsCoke_ModbusAddress = Config_GetValue("coke_modbus_address", 0);
130         giServer_Port        = Config_GetValue_Int("server_port", 0);
131         gsItemListFile       = Config_GetValue("items_file", 0);
132
133         gbNoCostMode         = (Config_GetValue_Bool("test_mode", 0) == 1);
134         gbSyslogEnabled      = (Config_GetValue_Bool("disable_syslog", 0) == 0);
135
136         signal(SIGINT, sigint_handler);
137         signal(SIGTERM, sigint_handler);
138         
139         openlog("odispense2", 0, LOG_LOCAL4);
140         
141         if( Bank_Initialise(gsCokebankPath) )
142                 return -1;
143
144         Init_Handlers();
145
146         Load_Itemlist();
147         
148         Server_Start();
149         
150         if(gTimerThread)
151                 pthread_kill(gTimerThread, SIGKILL);
152
153         return 0;
154 }
155
156 void *Periodic_Thread(void *Unused __attribute__((unused)))
157 {
158          int    i;
159         
160         for( ;; )
161         {
162                 sleep(10);      // Sleep for a while
163 //              printf("Periodic firing\n");
164                 for( i = 0; i < ciMaxPeriodics; i ++ )
165                 {
166                         if( gaPeriodicCalls[i].Function )
167                                 gaPeriodicCalls[i].Function();
168                 }
169         }
170         return NULL;
171 }
172
173 void StartPeriodicThread(void)
174 {
175         pthread_create( &gTimerThread, NULL, Periodic_Thread, NULL );
176 }
177
178 void AddPeriodicFunction(void (*Fcn)(void))
179 {
180         int i;
181         for( i = 0; i < ciMaxPeriodics; i ++ )
182         {
183                 if( gaPeriodicCalls[i].Function )       continue;
184                 gaPeriodicCalls[i].Function = Fcn;
185                 return ;
186         }
187         
188         fprintf(stderr, "Error: No space for %p in periodic list\n", Fcn);
189 }
190
191 int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage)
192 {
193          int    ret;
194         
195         ret = regexec(regex, string, nMatches, matches, 0);
196         if( ret == REG_NOMATCH ) {
197                 return -1;
198         }
199         if( ret ) {
200                 size_t  len = regerror(ret, regex, NULL, 0);
201                 char    errorStr[len];
202                 regerror(ret, regex, errorStr, len);
203                 printf("string = '%s'\n", string);
204                 fprintf(stderr, "%s\n%s", errorMessage, errorStr);
205                 exit(-1);
206         }
207         
208         return ret;
209 }
210
211 void CompileRegex(regex_t *regex, const char *pattern, int flags)
212 {
213          int    ret = regcomp(regex, pattern, flags);
214         if( ret ) {
215                 size_t  len = regerror(ret, regex, NULL, 0);
216                 char    errorStr[len];
217                 regerror(ret, regex, errorStr, len);
218                 fprintf(stderr, "Regex compilation failed - %s\n%s\n", errorStr, pattern);
219                 exit(-1);
220         }
221 }
222
223 // Serial helper
224 int InitSerial(const char *File, int BaudRate)
225 {
226         struct termios  info;
227          int    baud;
228          int    fd;
229         
230         fd = open(File, O_RDWR | O_NOCTTY | O_NONBLOCK);
231         if( fd == -1 )  return -1;
232         
233         switch(BaudRate)
234         {
235         case 1200:      baud = B1200;   break;
236         case 9600:      baud = B9600;   break;
237         case 115200:    baud = B115200; break;
238         default:
239                 fprintf(stderr, "ERROR: Invalid baud rate to InitSerial (%i)\n", BaudRate);
240                 exit(1);
241         }
242         
243         info.c_lflag = 0;       // Non-Canoical, No Echo
244         info.c_cflag = baud | CS8 | CLOCAL | CREAD;     // baud, 8N1
245         info.c_iflag = IGNCR;   // Ignore \r
246         info.c_oflag = 0;       // ???
247         cfsetspeed(&info, baud);
248         info.c_cc[VTIME] = 0;   // No time limit
249         info.c_cc[VMIN] = 1;    // Block until 1 char
250         
251         tcflush(fd, TCIFLUSH);
252         tcsetattr(fd, TCSANOW, &info);
253         
254         return fd;
255 }
256
257
258 /**
259  * \brief Create a formatted heap string
260  */
261 char *mkstr(const char *Format, ...)
262 {
263         va_list args;
264          int    len;
265         char    *ret;
266
267         va_start(args, Format);
268         len = vsnprintf(NULL, 0, Format, args);
269         va_end(args);
270
271         ret = malloc( len + 1 );
272         if(!ret)        return NULL;
273
274         va_start(args, Format);
275         vsprintf(ret, Format, args);
276         va_end(args);
277         
278         return ret;
279 }
280

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