Adding new config system, template modbus coke, AUTHIDENT in client
authorJohn Hodge <[email protected]>
Tue, 27 Dec 2011 09:34:32 +0000 (17:34 +0800)
committerJohn Hodge <[email protected]>
Tue, 27 Dec 2011 09:34:32 +0000 (17:34 +0800)
.gitignore
src/client/main.c
src/server/Makefile
src/server/common.h
src/server/config.c [new file with mode: 0644]
src/server/handler_coke.c
src/server/main.c
src/server/server.c

index 69b5969..6e59002 100644 (file)
@@ -1,5 +1,7 @@
 *.[od]
 *.so
 *~
+*.swp
 dispsrv
 dispense
+cokebank.log
index ce9a9af..595d3e6 100644 (file)
@@ -1314,28 +1314,53 @@ int OpenConnection(const char *Host, int Port)
        return sock;
 }
 
-/**
- * \brief Authenticate with the server
- * \return Boolean Failure
- */
-int Authenticate(int Socket)
+int Authenticate_AutoAuth(int Socket, const char *Username)
 {
-       struct passwd   *pwd;
        char    *buf;
         int    responseCode;
-       #if ATTEMPT_PASSWORD_AUTH
-       char    salt[32];
-        int    i;
-       regmatch_t      matches[4];
-       #endif
+        int    ret = -1;
        
-       if( gbIsAuthenticated ) return 0;
+       // Attempt automatic authentication
+       sendf(Socket, "AUTOAUTH %s\n", Username);
        
-       // Get user name
-       pwd = getpwuid( getuid() );
+       // Check if it worked
+       buf = ReadLine(Socket);
+       
+       responseCode = atoi(buf);
+       switch( responseCode )
+       {
+       case 200:       // Autoauth succeeded, return
+               ret = 0;
+               break;
+       
+       case 401:       // Untrusted
+               fprintf(stderr, "Untrusted host, AUTOAUTH unavaliable\n");
+               ret = RV_PERMISSIONS;
+               break;
+       case 404:       // Bad Username
+               fprintf(stderr, "Bad Username '%s'\n", Username);
+               ret = RV_INVALID_USER;
+               break;
+       
+       default:
+               fprintf(stderr, "Unkown response code %i from server\n", responseCode);
+               printf("%s\n", buf);
+               ret = RV_UNKNOWN_ERROR;
+               break;;
+       }
+       
+       free(buf);
+       return ret;
+}
+
+int Authenticate_AuthIdent(int Socket)
+{
+       char    *buf;
+        int    responseCode;
+        int    ret = -1;
        
        // Attempt automatic authentication
-       sendf(Socket, "AUTOAUTH %s\n", pwd->pw_name);
+       sendf(Socket, "AUTHIDENT\n");
        
        // Check if it worked
        buf = ReadLine(Socket);
@@ -1344,98 +1369,131 @@ int Authenticate(int Socket)
        switch( responseCode )
        {
        case 200:       // Autoauth succeeded, return
-               free(buf);
+               ret = 0;
                break;
        
-       case 401:       // Untrusted, attempt password authentication
-               free(buf);
-
-               #if ATTEMPT_PASSWORD_AUTH       
-               sendf(Socket, "USER %s\n", pwd->pw_name);
-               printf("Using username %s\n", pwd->pw_name);
-               
-               buf = ReadLine(Socket);
-               
-               // TODO: Get Salt
-               // Expected format: 100 SALT <something> ...
-               // OR             : 100 User Set
-               RunRegex(&gSaltRegex, buf, 4, matches, "Malformed server response");
-               responseCode = atoi(buf);
-               if( responseCode != 100 ) {
-                       fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
-                       free(buf);
-                       return RV_UNKNOWN_ERROR;        // ERROR
-               }
-               
-               // Check for salt
-               if( memcmp( buf+matches[2].rm_so, "SALT", matches[2].rm_eo - matches[2].rm_so) == 0) {
-                       // Store it for later
-                       memcpy( salt, buf + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so );
-                       salt[ matches[3].rm_eo - matches[3].rm_so ] = 0;
-               }
-               free(buf);
-               
-               // Give three attempts
-               for( i = 0; i < 3; i ++ )
-               {
-                        int    ofs = strlen(pwd->pw_name)+strlen(salt);
-                       char    tmpBuf[42];
-                       char    tmp[ofs+20];
-                       char    *pass = getpass("Password: ");
-                       uint8_t h[20];
-                       
-                       // Create hash string
-                       // <username><salt><hash>
-                       strcpy(tmp, pwd->pw_name);
-                       strcat(tmp, salt);
-                       SHA1( (unsigned char*)pass, strlen(pass), h );
-                       memcpy(tmp+ofs, h, 20);
-                       
-                       // Hash all that
-                       SHA1( (unsigned char*)tmp, ofs+20, h );
-                       sprintf(tmpBuf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-                               h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
-                               h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
-                               );
-               
-                       // Send password
-                       sendf(Socket, "PASS %s\n", tmpBuf);
-                       buf = ReadLine(Socket);
-               
-                       responseCode = atoi(buf);
-                       // Auth OK?
-                       if( responseCode == 200 )       break;
-                       // Bad username/password
-                       if( responseCode == 401 )       continue;
-                       
-                       fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
-                       free(buf);
-                       return RV_UNKNOWN_ERROR;
-               }
-               free(buf);
-               if( i == 3 )
-                       return RV_INVALID_USER; // 2 = Bad Password
-               
-               #else
+       case 401:       // Untrusted
                fprintf(stderr, "Untrusted host, AUTOAUTH unavaliable\n");
-               return RV_INVALID_USER;
-               #endif
+               ret = RV_PERMISSIONS;
                break;
        
-       case 404:       // Bad Username
-               fprintf(stderr, "Bad Username '%s'\n", pwd->pw_name);
-               free(buf);
-               return RV_INVALID_USER;
-       
        default:
                fprintf(stderr, "Unkown response code %i from server\n", responseCode);
                printf("%s\n", buf);
+               ret = RV_UNKNOWN_RESPONSE;
+               break;
+       }
+       
+       free(buf);
+
+       return ret;
+}
+
+int Authenticate_Password(int Socket, const char *Username)
+{
+       #if USE_PASSWORD_AUTH
+       char    *buf;
+        int    responseCode;   
+       char    salt[32];
+        int    i;
+       regmatch_t      matches[4];
+
+       sendf(Socket, "USER %s\n", Username);
+       printf("Using username %s\n", Username);
+       
+       buf = ReadLine(Socket);
+       
+       // TODO: Get Salt
+       // Expected format: 100 SALT <something> ...
+       // OR             : 100 User Set
+       RunRegex(&gSaltRegex, buf, 4, matches, "Malformed server response");
+       responseCode = atoi(buf);
+       if( responseCode != 100 ) {
+               fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
                free(buf);
-               return RV_UNKNOWN_ERROR;
+               return RV_UNKNOWN_ERROR;        // ERROR
+       }
+       
+       // Check for salt
+       if( memcmp( buf+matches[2].rm_so, "SALT", matches[2].rm_eo - matches[2].rm_so) == 0) {
+               // Store it for later
+               memcpy( salt, buf + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so );
+               salt[ matches[3].rm_eo - matches[3].rm_so ] = 0;
+       }
+       free(buf);
+       
+       // Give three attempts
+       for( i = 0; i < 3; i ++ )
+       {
+                int    ofs = strlen(Username)+strlen(salt);
+               char    tmpBuf[42];
+               char    tmp[ofs+20];
+               char    *pass = getpass("Password: ");
+               uint8_t h[20];
+               
+               // Create hash string
+               // <username><salt><hash>
+               strcpy(tmp, Username);
+               strcat(tmp, salt);
+               SHA1( (unsigned char*)pass, strlen(pass), h );
+               memcpy(tmp+ofs, h, 20);
+               
+               // Hash all that
+               SHA1( (unsigned char*)tmp, ofs+20, h );
+               sprintf(tmpBuf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+                       h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
+                       h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
+                       );
+       
+               // Send password
+               sendf(Socket, "PASS %s\n", tmpBuf);
+               buf = ReadLine(Socket);
+       
+               responseCode = atoi(buf);
+               // Auth OK?
+               if( responseCode == 200 )       break;
+               // Bad username/password
+               if( responseCode == 401 )       continue;
+               
+               fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
+               free(buf);
+               return -1;
        }
+       free(buf);
+       if( i == 3 )
+               return RV_INVALID_USER; // 2 = Bad Password
+
+       return 0;
+       #else
+       return RV_INVALID_USER;
+       #endif
+}
+
+/**
+ * \brief Authenticate with the server
+ * \return Boolean Failure
+ */
+int Authenticate(int Socket)
+{
+       struct passwd   *pwd;
+       
+       if( gbIsAuthenticated ) return 0;
        
+       // Get user name
+       pwd = getpwuid( getuid() );
+
+       // Attempt AUTOAUTH
+       if( Authenticate_AutoAuth(Socket, pwd->pw_name) == 0 )
+               ;
+       else if( Authenticate_AuthIdent(Socket) == 0 )
+               ;
+       else if( Authenticate_Password(Socket, pwd->pw_name) == 0 )
+               return RV_INVALID_USER;
+
        // Set effective user
        if( gsEffectiveUser ) {
+               char    *buf;
+                int    responseCode;
                sendf(Socket, "SETEUSER %s\n", gsEffectiveUser);
                
                buf = ReadLine(Socket);
index 6acb88c..300145d 100644 (file)
@@ -1,16 +1,18 @@
 # OpenDispense 2
 #
 
-OBJ := main.o server.o logging.o
+INSTALLDIR := /usr/local/opendispense2
+
+OBJ := main.o server.o logging.o config.o
 OBJ += dispense.o itemdb.o
 OBJ += handler_coke.o handler_snack.o handler_door.o
 BIN := ../../dispsrv
 
 DEPFILES := $(OBJ:%.o=%.d)
 
-LINKFLAGS := -g ../../cokebank.so -lutil -lident -Wl,-rpath,. -Wl,-rpath,/usr/local/sbin
+LINKFLAGS := -g ../../cokebank.so -lutil -lident -lmodbus -Wl,-rpath,. -Wl,-rpath,$(INSTALLDIR)
 CPPFLAGS := 
-CFLAGS := -Wall -Wextra -Werror -g
+CFLAGS := -Wall -Wextra -Werror -g -std=gnu99
 
 .PHONY: all clean
 
@@ -20,7 +22,7 @@ clean:
        $(RM) $(BIN) $(OBJ)
 
 $(BIN): $(OBJ)
-       $(CC) -o $(BIN) $(LINKFLAGS) $(OBJ)
+       $(CC) -o $(BIN) $(OBJ) $(LINKFLAGS)
 
 %.o: %.c
        $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS)
index 19337e5..004fbf8 100644 (file)
@@ -22,6 +22,8 @@
 
 #define UNUSED(var)    unused__##var __attribute__((__unused__))
 
+#define ASSERT(cnd) do{if(!(cnd)){fprintf(stderr, "ASSERT failed at "__FILE__":"EXPSTR(__LINE__)" - "EXPSTR(cnd)"\n");exit(-1);}}while(0)
+
 // === STRUCTURES ===
 typedef struct sItem   tItem;
 typedef struct sUser   tUser;
@@ -96,4 +98,12 @@ extern int   DispenseUpdateItem(int User, tItem *Item, const char *NewName, int Ne
 extern void    Log_Error(const char *Format, ...);
 extern void    Log_Info(const char *Format, ...);
 
+// --- Config Database ---
+extern void    Config_ParseFile(const char *Filename);
+extern void    Config_AddValue(const char *Key, const char *Value);
+extern int     Config_GetValueCount(const char *KeyName);
+extern const char      *Config_GetValue(const char *KeyName, int Index);
+extern int     Config_GetValue_Bool(const char *KeyName, int Index);
+extern int     Config_GetValue_Int(const char *KeyName, int Index);
+
 #endif
diff --git a/src/server/config.c b/src/server/config.c
new file mode 100644 (file)
index 0000000..4630b58
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * OpenDispense 2 
+ * UCC (University [of WA] Computer Club) Electronic Accounting System
+ *
+ * config.c - Configuration file parser
+ *
+ * This file is licenced under the 3-clause BSD Licence. See the file
+ * COPYING for full details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+#include <regex.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAX_LINE_LEN   128
+
+// === TYPES ===
+typedef struct sConfigValue    tConfigValue;
+typedef struct sConfigKey      tConfigKey;
+
+// === STRUCTURES ===
+struct sConfigValue
+{
+       tConfigValue    *Next;
+       char    Data[];
+};
+
+struct sConfigKey
+{
+       tConfigKey      *NextKey;
+       tConfigValue    *FirstValue;
+       tConfigValue    *LastValue;
+        int    ValueCount;
+       char    KeyName[];
+};
+
+// === PROTOTYPES ===
+void   Config_ParseFile(const char *Filename);
+void   Config_AddValue(const char *Key, const char *Value);
+void   Config_int_AddValueToKey(tConfigKey *Key, const char *Value);
+tConfigKey     *Config_int_GetKey(const char *KeyName, int bCreate);
+ int   Config_GetValueCount(const char *KeyName);
+const char     *Config_GetValue(const char *KeyName, int Index);
+
+// === GLOBALS ===
+tConfigKey     *gConfig;
+
+// === CODE ===
+void Config_ParseFile(const char *Filename)
+{
+       FILE    *fp;
+       char    line[MAX_LINE_LEN];
+       regex_t regexp_option;
+       regex_t regexp_empty;
+
+       CompileRegex(&regexp_option, "^\\s*([^ \t]+)\\s+(.+)$", REG_EXTENDED);  //
+       CompileRegex(&regexp_empty, "^\\s*$", REG_EXTENDED);    //
+       
+       fp = fopen(Filename, "r");
+       if(!fp) {
+               fprintf(stderr, "Unable to open config file '%s'\n", Filename);
+               perror("Config_ParseFile");
+               exit(-1);
+       }
+       
+       while( fgets(line, sizeof(line), fp) )
+       {
+               regmatch_t      matches[3];
+
+               // Trim and clean up
+               {
+                        int    i;
+                       for( i = 0; line[i]; i ++ )
+                       {
+                               if( line[i] == '#' || line[i] == ';' ) {
+                                       line[i] = '\0';
+                                       break;
+                               }
+                       }
+                       
+                       while( i --, isspace(line[i]) )
+                               line[i] = 0;
+               }
+               
+                               
+               if( regexec(&regexp_empty, line, 1, matches, 0) == 0 )
+                       continue ;
+
+               if( RunRegex(&regexp_option, line, 3, matches, "Parsing configuration file") )
+               {
+                       fprintf(stderr, "Syntax error\n- %s", line);
+                       continue ;
+               }
+               
+               line[matches[1].rm_eo] = 0;
+               line[matches[2].rm_eo] = 0;
+       
+               Config_AddValue(line + matches[1].rm_so, line + matches[2].rm_so);
+       }
+       
+       fclose(fp);
+       regfree(&regexp_option);
+       regfree(&regexp_empty);
+}
+
+void Config_AddValue(const char *Key, const char *Value)
+{
+       tConfigKey      *key;
+       
+       // Find key (creating if needed)
+       key = Config_int_GetKey(Key, 1);
+
+       Config_int_AddValueToKey(key, Value);   
+}
+
+void Config_int_AddValueToKey(tConfigKey *Key, const char *Value)
+{
+       tConfigValue    *newVal;
+       // Create value
+       newVal = malloc(sizeof(tConfigValue) + strlen(Value) + 1);
+       newVal->Next = NULL;
+       strcpy(newVal->Data, Value);
+       
+       #if 1
+       // Add to the end of the key's list
+       if(Key->LastValue)
+               Key->LastValue->Next = newVal;
+       else
+               Key->FirstValue = newVal;
+       Key->LastValue = newVal;
+       #else
+       // Add to the start of the key's list
+       if(Key->LastValue == NULL)
+               Key->LastValue = newVal;
+       newVal->Next = Key->FirstValue;
+       Key->FirstValue = newVal;
+       #endif
+       Key->ValueCount ++;
+}
+
+/**
+ * \brief 
+ */
+tConfigKey *Config_int_GetKey(const char *KeyName, int bCreate)
+{
+       tConfigKey      *key, *prev = NULL;
+       
+       // Search the sorted list of keys
+       for( key = gConfig; key; prev = key, key = key->NextKey )
+       {
+               int cmp = strcmp(key->KeyName, KeyName);
+               if(cmp == 0)    return key;     // Equal, return
+               if(cmp > 0)     break;  // Greater, fast exit
+       }
+       
+       if( bCreate )
+       {
+               // Create new key
+               key = malloc(sizeof(tConfigKey) + strlen(KeyName) + 1);
+               key->FirstValue = NULL;
+               key->LastValue = NULL;
+               key->ValueCount = 0;
+               strcpy(key->KeyName, KeyName);
+               
+               // Append
+               if(prev) {
+                       key->NextKey = prev->NextKey;
+                       prev->NextKey = key;
+               }
+               else {
+                       key->NextKey = gConfig;
+                       gConfig = key;
+               }
+       }
+       else
+       {
+               key = NULL;
+       }
+       
+       return key;
+}
+
+int Config_GetValueCount(const char *KeyName)
+{
+       tConfigKey      *key = Config_int_GetKey(KeyName, 0);
+       if(!key)        return 0;
+       
+       return key->ValueCount;
+}
+
+const char *Config_GetValue(const char *KeyName, int Index)
+{
+       tConfigKey      *key;
+       tConfigValue    *val;   
+
+       key = Config_int_GetKey(KeyName, 0);
+       if(!key) {
+               fprintf(stderr, "Unknown key '%s'\n", KeyName);
+               exit(1);
+               return NULL;
+       }
+       
+       if(Index < 0 || Index >= key->ValueCount)       return NULL;
+       
+       for( val = key->FirstValue; Index && val; val = val->Next, Index -- );
+
+       ASSERT(val != NULL);
+       
+       return val->Data;
+}
+
+int Config_GetValue_Bool(const char *KeyName, int Index)
+{
+       const char *val = Config_GetValue(KeyName, Index);
+       if(!val)        return -1;
+       
+       if( atoi(val) == 1 )    return 1;
+       if( val[0] == '0' && val[1] == '\0' )   return 0;
+       
+       if( strcasecmp(val, "true") == 0 )      return 1;
+       if( strcasecmp(val, "false") == 0 )     return 0;
+       
+       if( strcasecmp(val, "yes") == 0 )       return 1;
+       if( strcasecmp(val, "no") == 0 )        return 0;
+       
+       return -1;
+}
+
+int Config_GetValue_Int(const char *KeyName, int Index)
+{
+        int    tmp;
+       const char *val = Config_GetValue(KeyName, Index);
+       if(!val)        return -1;
+       
+       if( (tmp = atoi(val)) ) return tmp;
+       if( val[0] == '0' && val[1] == '\0' )   return 0;
+       
+       return -1;
+}
+
index fffa7b6..21d61f6 100644 (file)
 #include "common.h"
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <regex.h>
 #include <stdarg.h>
 #include <pthread.h>
+#include <unistd.h>
+#include <modbus/modbus.h>
 
-#define READ_TIMEOUT   2       // 2 seconds for ReadChar
-#define TRACE_COKE     0
+#define MIN_DISPENSE_PERIOD    5
 
-#if TRACE_COKE
-# define TRACE(v...) do{printf("%s: ",__func__);printf(v);}while(0)
-#else
-# define TRACE(...)
-#endif
+// === CONSTANTS ===
+const int      ciCoke_MinPeriod = 5;
+const int      ciCoke_DropBitBase = 0;
+const int      ciCoke_StatusBitBase = 0;
 
 // === IMPORTS ===
 
 // === PROTOTYPES ===
  int   Coke_InitHandler();
- int   Coke_int_GetSlotStatus(char *Buffer, int Slot);
-void   Coke_int_UpdateSlotStatuses(void);
  int   Coke_CanDispense(int User, int Item);
  int   Coke_DoDispense(int User, int Item);
- int   Writef(const char *Format, ...);
- int   WaitForColon();
- int   ReadLine(int len, char *output);
 
 // === GLOBALS ===
 tHandler       gCoke_Handler = {
@@ -48,152 +39,58 @@ tHandler   gCoke_Handler = {
        Coke_CanDispense,
        Coke_DoDispense
 };
-char   *gsCoke_SerialPort = "/dev/ttyS0";
- int   giCoke_SerialFD;
-regex_t        gCoke_StatusRegex;
- int   gaCoke_CachedStatus[7];
-pthread_mutex_t        gCoke_Mutex = PTHREAD_MUTEX_INITIALIZER;
+const char     *gsCoke_ModbusAddress = "130.95.13.73";
+modbus_t       *gCoke_Modbus;
 time_t gtCoke_LastDispenseTime;
+ int   gbCoke_DummyMode = 1;
 
 // == CODE ===
 int Coke_InitHandler()
 {
-       CompileRegex(&gCoke_StatusRegex, "^slot\\s+([0-9]+)\\s+([^:]+):([a-zA-Z]+)\\s*", REG_EXTENDED);
-       
-       printf("Connecting to coke machine on '%s'\n", gsCoke_SerialPort);
-       
-       giCoke_SerialFD = InitSerial(gsCoke_SerialPort, 9600);
-       if( giCoke_SerialFD == -1 ) {
-               fprintf(stderr, "ERROR: Unable to open coke serial port ('%s')\n", gsCoke_SerialPort);
-       }
-       else {
-               int i;
-               for( i = 0; i < 7; i ++ )
-                       gaCoke_CachedStatus[i] = -1;
-               // Reset the slot names.
-               // - Dunno why this is needed, but the machine plays silly
-               //   sometimes.
-               Writef("\r\n\r\n");
-               WaitForColon();
-               Writef("n0 Slot0\r\n");
-               if( !WaitForColon() )
-               {
-                       Writef("n1 Slot1\r\n");
-                       WaitForColon();
-                       Writef("n2 Slot2\r\n");
-                       WaitForColon();
-                       Writef("n3 Slot3\r\n");
-                       WaitForColon();
-                       Writef("n4 Slot4\r\n");
-                       WaitForColon();
-                       Writef("n5 Slot5\r\n");
-                       WaitForColon();
-                       Writef("n6 Coke\r\n");
-                       WaitForColon();
-                       
-                       Coke_int_UpdateSlotStatuses();
-               }
-               else
-                       fprintf(stderr, "Coke machine timed out.\n");
-       }
-       
-       AddPeriodicFunction(Coke_int_UpdateSlotStatuses);
-       
-       return 0;
-}
+       printf("Connecting to coke machine on '%s'\n", gsCoke_ModbusAddress);
 
-int Coke_int_GetSlotStatus(char *Buffer, int Slot)
-{
-       regmatch_t      matches[4];
-        int    ret;
-       char    *status;        
-       
-       // Parse status response
-       ret = RunRegex(&gCoke_StatusRegex, Buffer, sizeof(matches)/sizeof(matches[0]), matches, "Bad Response");
-       if( ret ) {
-               return -1;
+       // Configuable dummy/blank mode (all dispenses succeed)
+       // TODO: Find a better way of handling missing/invalid options
+       if( Config_GetValueCount("coke_dummy_mode") > 0 )
+       {
+               gbCoke_DummyMode = Config_GetValue_Bool("coke_dummy_mode", 0);
+               if(gbCoke_DummyMode == -1)      gbCoke_DummyMode = 0;
        }
 
-       // Get slot status
-       Buffer[ matches[3].rm_eo ] = '\0';
-       status = &Buffer[ matches[3].rm_so ];
-       
-       TRACE("Machine responded slot %i status '%s'\n", Slot, status);
-
-       if( strcmp(status, "full") == 0 ) {
-               gaCoke_CachedStatus[Slot] = 0;  // 0: Avaliiable
-               return 0;
-       }
-       else {
-               gaCoke_CachedStatus[Slot] = 1;  // 1: Empty
-               return 1;
+       // Open modbus
+       modbus_new_tcp(gsCoke_ModbusAddress, 502);
+       if( !gCoke_Modbus )
+       {
+               perror("coke - modbus_new_tcp");
        }
-}
-
-/**
- * \brief Update the status of all coke slots
- * \note Uses goto to reduce the chance of the lock being kept
- */
-void Coke_int_UpdateSlotStatuses(void)
-{
-        int    i, len;
-       char    tmp[40];
-       
-       if( giCoke_SerialFD == -1 )     return ;
-       
-       pthread_mutex_lock(&gCoke_Mutex);
-       
-       while( ReadLine(sizeof tmp, tmp) >= 0 ) ;
-       TRACE("send d7\n");
-       Writef("d7\r\n");       // Update slot statuses
-       if( WaitForColon() )    goto ret;
-       TRACE("send s\n");
-       Writef("s\r\n");
-       do {
-               i = ReadLine(sizeof tmp, tmp);  // Read back what we just said
-               if( i == -1 ) {
-                       TRACE("Eat read failed");
-                       goto ret;
-               }
-       } while(tmp[0] != ':' && tmp[1] != 's');
-       
-       for( i = 0; i <= 6; i ++ )
+       else
        {
-               // Read until non-blank line
-               while( (len = ReadLine(sizeof tmp, tmp)) == 0 ) ;
-               if( len == -1 ) {
-                       TRACE("Read failed on slot %i\n", i);
-                       goto ret;       // I give up :(
+               if( modbus_connect(gCoke_Modbus) )
+               {
+                       perror("coke - modbus_connect");
                }
-               TRACE("tmp = '%s'\n", tmp);
-               Coke_int_GetSlotStatus(tmp, i);
-       }
-       // Eat blank line
-       len = ReadLine(sizeof tmp, tmp);
-       if( len == -1 ) {
-               TRACE("Read failed on blank line\n");
        }
 
-       TRACE("Updated\n");
-
-ret:
-       pthread_mutex_unlock(&gCoke_Mutex);
+       return 0;
 }
 
 int Coke_CanDispense(int UNUSED(User), int Item)
 {
+       uint8_t status;
        // Sanity please
-       if( Item < 0 || Item > 6 )      return -1;      // -EYOURBAD
+       if( Item < 0 || Item > 6 )      return -1;
+
+       // Check for 'dummy' mode
+       if( gbCoke_DummyMode )
+               return 0;
        
        // Can't dispense if the machine is not connected
-       if( giCoke_SerialFD == -1 )
+       if( !gCoke_Modbus )
                return -2;
 
-       // HACK!
-       // 2011-10-21: The sensors in slot 1 and 3 a bad, just ignore the sensor result
-       if( Item == 1 || Item == 3 )    return 0;
+       modbus_read_bits(gCoke_Modbus, ciCoke_StatusBitBase + Item, 1, &status);
 
-       return gaCoke_CachedStatus[Item];
+       return status == 0;
 }
 
 /**
@@ -201,195 +98,29 @@ int Coke_CanDispense(int UNUSED(User), int Item)
  */
 int Coke_DoDispense(int UNUSED(User), int Item)
 {
-       char    tmp[32];
-        int    ret, len;
-
        // Sanity please
        if( Item < 0 || Item > 6 )      return -1;
 
+       // Check for 'dummy' mode
+       if( gbCoke_DummyMode )
+               return 0;
+       
        // Can't dispense if the machine is not connected
-       if( giCoke_SerialFD == -1 )
+       if( !gCoke_Modbus )
                return -2;
 
        // Make sure there are not two dispenses within n seconds
-       if( time(NULL) - gtCoke_LastDispenseTime < 10 )
+       if( time(NULL) - gtCoke_LastDispenseTime < ciCoke_MinPeriod )
        {
-               printf("Wait %li seconds?\n", 10 - (time(NULL) - gtCoke_LastDispenseTime));
-               sleep( 10 - (time(NULL) - gtCoke_LastDispenseTime) );
+                int    delay = ciCoke_MinPeriod - (time(NULL) - gtCoke_LastDispenseTime);
+               printf("Wait %i seconds?\n", delay);
+               sleep( delay );
                printf("wait done\n");
        }
        
-       // LOCK
-       pthread_mutex_lock(&gCoke_Mutex);
-       
-       TRACE("flushing input\n");
-       
-
-       // Wait for prompt
-       ret = 0;
-       while( WaitForColon() && ret < 3 )
-       {
-               // Flush the input buffer
-               char    tmpbuf[512];
-               read(giCoke_SerialFD, tmpbuf, sizeof(tmpbuf));
-               TRACE("sending 'd7'\n");
-               Writef("d7\r\n");
-               ret ++;
-       }
-       if( ret == 3 )
-       {
-               TRACE("timed out\n");
-               pthread_mutex_unlock(&gCoke_Mutex);
-               return -1;
-       }
-
-       TRACE("sending 'd%i'\n", Item);
-       // Dispense
-       Writef("d%i\r\n", Item);
-       
-       // Read empty lines and echo-backs
-       do {
-               ret = ReadLine(sizeof(tmp)-1, tmp);
-               if( ret == -1 ) {
-                       pthread_mutex_unlock(&gCoke_Mutex);
-                       return -1;
-               }
-               TRACE("read %i '%s'\n", ret, tmp);
-       } while( ret == 0 || tmp[0] == ':' || tmp[0] == 'd' );
-
-       WaitForColon(); // Eat up rest of response
-       
-       TRACE("done\n");
-
-       // TODO: Regex instead?
-       if( strcmp(tmp, "ok") == 0 ) {
-               // We think dispense worked
-               // - The machine returns 'ok' if an empty slot is dispensed, even if
-               //   it doesn't actually try to dispense (no sound)
-               ret = 0;
-       }
-       else {
-               printf("Coke_DoDispense: Machine returned unknown value '%s'\n", tmp);
-               ret = -1;
-       }
-       
-       TRACE("Updating slot status\n");
-       
-       // Update status
-       WaitForColon();
-       Writef("s%i\r\n", Item);
-       len = ReadLine(sizeof tmp, tmp);
-       if(len == -1)   gaCoke_CachedStatus[Item] = -1;
-       Coke_int_GetSlotStatus(tmp, Item);
-       {
-               char buf[512];
-               read(giCoke_SerialFD, buf, 512);        // Flush
-       }
-
-       gtCoke_LastDispenseTime = time(NULL);
-       
-       // Release and return
-       pthread_mutex_unlock(&gCoke_Mutex);
-       
-       //return ret;
-       // HACK!!!
-       return 0;
-}
-
-char ReadChar()
-{
-       fd_set  readfs;
-       char    ch = 0;
-        int    ret;
-       struct timeval  timeout;
-       
-       timeout.tv_sec = READ_TIMEOUT;
-       timeout.tv_usec = 0;
-       
-       FD_ZERO(&readfs);
-       FD_SET(giCoke_SerialFD, &readfs);
-       
-       ret = select(giCoke_SerialFD+1, &readfs, NULL, NULL, &timeout);
-       if( ret == 0 ) {
-               fprintf(stderr, "ReadChar: Timeout of %is expired\n", READ_TIMEOUT);
-               return 0;       // Timeout
-       }
-       if( ret != 1 ) {
-               printf("ReadChar: select return %i\n", ret);
-               return 0;
-       }
-       
-       ret = read(giCoke_SerialFD, &ch, 1);
-       if( ret != 1 ) {
-               printf("ReadChar: ret != 1 (%i)\n", ret);
-               return 0;
-       }
-       
-       return ch;
-}
-
-int Writef(const char *Format, ...)
-{
-       va_list args;
-        int    len;
-       
-       va_start(args, Format);
-       len = vsnprintf(NULL, 0, Format, args);
-       va_end(args);
-       
-       {
-               char    buf[len+1];
-               va_start(args, Format);
-               vsnprintf(buf, len+1, Format, args);
-               va_end(args);
-               
-               #if DEBUG
-               printf("Writef: %s", buf);
-               #endif
-               
-               return write(giCoke_SerialFD, buf, len);
-       }
-       
-}
-
-int WaitForColon()
-{
-       fd_set  readfs;
-       char    ch = 0;
-       
-       FD_SET(giCoke_SerialFD, &readfs);
-       
-       while( (ch = ReadChar()) != ':' && ch != 0);
-       
-       if( ch == 0 )   return -1;      // Timeout
+       // Dispense (with locking)
+       modbus_write_bit(gCoke_Modbus, ciCoke_DropBitBase + Item, 1);
        
        return 0;
 }
 
-int ReadLine(int len, char *output)
-{
-       char    ch;
-        int    i = 0;
-       
-       for(;;)
-       {
-               ch = ReadChar();
-                       
-               if( i < len )
-                       output[i++] = ch;
-               
-               if( ch == '\0' ) {
-                       break;
-               }
-               if( ch == '\n' || ch == '\r' ) {
-                       if( i < len )
-                               output[--i] = '\0';
-                       break;
-               }
-       }
-
-       if( !ch )       return -1;
-       return i;
-}
-
-
index 590e197..c2ef55c 100644 (file)
@@ -27,17 +27,16 @@ extern void Load_Itemlist(void);
 extern void    Server_Start(void);
 extern int     gbServer_RunInBackground;
 extern int     giServer_Port;
-extern char    *gsItemListFile;
-extern char    *gsCoke_SerialPort;
-extern char    *gsSnack_SerialPort;
-extern char    *gsDoor_SerialPort;
+extern const char      *gsItemListFile;
+extern const char      *gsCoke_ModbusAddress;
+extern const char      *gsDoor_SerialPort;
 
 // === PROTOTYPES ===
 void   *Periodic_Thread(void *Unused);
 
 // === GLOBALS ===
  int   giDebugLevel = 0;
-char   *gsCokebankPath = "cokebank.db";
+const char     *gsCokebankPath = "cokebank.db";
 // - Functions called every 20s (or so)
 #define ciMaxPeriodics 10
 struct sPeriodicCall {
@@ -54,24 +53,16 @@ void sigint_handler()
 void PrintUsage(const char *progname)
 {
        fprintf(stderr, "Usage: %s\n", progname);
-       fprintf(stderr, "  -p    Set server port (default 11020)\n");
        fprintf(stderr, "  -d    Set debug level (0 - 2, default 0)\n");
-       fprintf(stderr, "  --itemsfile\n");
-       fprintf(stderr, "        Set debug level (0 - 2, default 0)\n");
-       fprintf(stderr, "  --cokeport\n");
-       fprintf(stderr, "        Coke machine serial port (Default \"/dev/ttyS0\")\n");
-       fprintf(stderr, "  --doorport\n");
-       fprintf(stderr, "        Door modem/relay serial port (Default \"/dev/ttyS3\")\n");
-       fprintf(stderr, "  --cokebank\n");
-       fprintf(stderr, "        Coke bank database file (Default \"cokebank.db\")\n");
        fprintf(stderr, "  --[dont-]daemonise\n");
-       fprintf(stderr, "        Run (or explicitly don't) the server disconnected from the terminal\n");
+       fprintf(stderr, "        Run (or explicitly don't run) the server disconnected from the terminal\n");
 }
 
 int main(int argc, char *argv[])
 {
         int    i;
-       
+       const char      *config_file = "dispsrv.conf";
+
        // Parse Arguments
        for( i = 1; i < argc; i++ )
        {
@@ -80,62 +71,56 @@ int main(int argc, char *argv[])
                {
                        switch(arg[1])
                        {
-                       case 'p':
+                       case 'f':
                                if( i + 1 >= argc )     return -1;
-                               giServer_Port = atoi(argv[++i]);
+                               config_file = argv[++i];
                                break;
                        case 'd':
                                if( i + 1 >= argc )     return -1;
-                               giDebugLevel = atoi(argv[++i]);
+                               Config_AddValue("debug_level", argv[++i]);
+                               giDebugLevel = atoi(argv[i]);
                                break;
-                       case 'D':
-                               gbServer_RunInBackground = 1;
-                               return -1;
                        default:
-                               // Usage Error?
+                               // Usage Error
                                PrintUsage(argv[0]);
                                return -1;
                        }
                }
-               else if( arg[0] == '-' && arg[1] == '-' ) {
-                       if( strcmp(arg, "--itemsfile") == 0 ) {
-                               if( i + 1 >= argc )     return -1;
-                               gsItemListFile = argv[++i];
-                       }
-                       else if( strcmp(arg, "--cokeport") == 0 ) {
-                               if( i + 1 >= argc )     return -1;
-                               gsCoke_SerialPort = argv[++i];
-                       }
-                       else if( strcmp(arg, "--snackport") == 0 ) {
-                               if( i + 1 >= argc )     return -1;
-                               gsSnack_SerialPort = argv[++i];
-                       }
-                       else if( strcmp(arg, "--doorport") == 0 ) {
-                               if( i + 1 >= argc )     return -1;
-                               gsDoor_SerialPort = argv[++i];
-                       }
-                       else if( strcmp(arg, "--cokebank") == 0 ) {
+               else if( arg[0] == '-' && arg[1] == '-' )
+               {
+                       if( strcmp(arg, "--configfile") == 0 ) {
                                if( i + 1 >= argc )     return -1;
-                               gsCokebankPath = argv[++i];
+                               config_file = argv[++i];
                        }
                        else if( strcmp(arg, "--daemonise") == 0 ) {
-                               gbServer_RunInBackground = 1;
+                               Config_AddValue("daemonise", "true");
                        }
                        else if( strcmp(arg, "--dont-daemonise") == 0 ) {
-                               gbServer_RunInBackground = 0;
+                               Config_AddValue("daemonise", "false");
                        }
                        else {
-                               // Usage error?
+                               // Usage error
                                PrintUsage(argv[0]);
                                return -1;
                        }
                }
-               else {
-                       // Usage Error?
+               else
+               {
+                       // Usage Error
                        PrintUsage(argv[0]);
                        return -1;
                }
        }
+
+       Config_ParseFile( config_file );
+
+       // Parse config values
+       gbServer_RunInBackground = Config_GetValue_Bool("daemonise", 0);
+       gsCokebankPath       = Config_GetValue("cokebank_database", 0);
+       gsDoor_SerialPort    = Config_GetValue("door_serial_port", 0);
+       gsCoke_ModbusAddress = Config_GetValue("coke_modbus_address", 0);
+       giServer_Port        = Config_GetValue_Int("server_port", 0);
+       gsItemListFile       = Config_GetValue("items_file", 0);
        
        signal(SIGINT, sigint_handler);
        signal(SIGTERM, sigint_handler);
@@ -157,10 +142,9 @@ int main(int argc, char *argv[])
        return 0;
 }
 
-void *Periodic_Thread(void *Unused)
+void *Periodic_Thread(void *Unused __attribute__((unused)))
 {
         int    i;
-       Unused = NULL;  // quiet, gcc
        
        for( ;; )
        {
@@ -220,7 +204,7 @@ void CompileRegex(regex_t *regex, const char *pattern, int flags)
                size_t  len = regerror(ret, regex, NULL, 0);
                char    errorStr[len];
                regerror(ret, regex, errorStr, len);
-               fprintf(stderr, "Regex compilation failed - %s\n", errorStr);
+               fprintf(stderr, "Regex compilation failed - %s\n%s\n", errorStr, pattern);
                exit(-1);
        }
 }
index b7ca068..05ee547 100644 (file)
@@ -44,7 +44,8 @@ typedef struct sClient
         int    Socket; // Client socket ID
         int    ID;     // Client ID
         
-        int    bIsTrusted;     // Is the connection from a trusted host/port
+        int    bTrustedHost;
+        int    bCanAutoAuth;   // Is the connection from a trusted host/port
        
        char    *Username;
        char    Salt[9];
@@ -57,7 +58,7 @@ typedef struct sClient
 // === PROTOTYPES ===
 void   Server_Start(void);
 void   Server_Cleanup(void);
-void   Server_HandleClient(int Socket, int bTrusted);
+void   Server_HandleClient(int Socket, int bTrustedHost, int bRootPort);
 void   Server_ParseClientCommand(tClient *Client, char *CommandString);
 // --- Commands ---
 void   Server_Cmd_USER(tClient *Client, char *Args);
@@ -118,6 +119,8 @@ const struct sClientCommand {
  int   gbServer_RunInBackground = 0;
 char   *gsServer_LogFile = "/var/log/dispsrv.log";
 char   *gsServer_ErrorLog = "/var/log/dispsrv.err";
+ int   giServer_NumTrustedHosts;
+struct in_addr *gaServer_TrustedHosts;
 // - State variables
  int   giServer_Socket;        // Server socket
  int   giServer_NextClientID = 1;      // Debug client ID
@@ -132,6 +135,19 @@ void Server_Start(void)
         int    client_socket;
        struct sockaddr_in      server_addr, client_addr;
 
+       // Parse trusted hosts list
+       giServer_NumTrustedHosts = Config_GetValueCount("trusted_host");
+       gaServer_TrustedHosts = malloc(giServer_NumTrustedHosts * sizeof(*gaServer_TrustedHosts));
+       for( int i = 0; i < giServer_NumTrustedHosts; i ++ )
+       {
+               const char      *addr = Config_GetValue("trusted_host", i);
+               
+               if( inet_aton(addr, &gaServer_TrustedHosts[i]) == 0 ) {
+                       fprintf(stderr, "Invalid IP address '%s'\n", addr);
+                       continue ;
+               }
+       }
+
        atexit(Server_Cleanup);
        // Ignore SIGPIPE (stops crashes when the client exits early)
        signal(SIGPIPE, SIG_IGN);
@@ -210,6 +226,7 @@ void Server_Start(void)
        {
                uint    len = sizeof(client_addr);
                 int    bTrusted = 0;
+                int    bRootPort = 0;
                
                // Accept a connection
                client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
@@ -241,9 +258,22 @@ void Server_Start(void)
                // Doesn't matter what, localhost is trusted
                if( ntohl( client_addr.sin_addr.s_addr ) == 0x7F000001 )
                        bTrusted = 1;
-               
-               // Trusted Connections
+       
+               // Check if the host is on the trusted list     
+               for( int i = 0; i < giServer_NumTrustedHosts; i ++ )
+               {
+                       if( memcmp(&client_addr.sin_addr, &gaServer_TrustedHosts[i], sizeof(struct in_addr)) == 0 )
+                       {
+                               bTrusted = 1;
+                               break;
+                       }
+               }
+
+               // Root port (can AUTOAUTH if also a trusted machine
                if( ntohs(client_addr.sin_port) < 1024 )
+                       bRootPort = 1;
+               
+               #if 0
                {
                        // TODO: Make this runtime configurable
                        switch( ntohl( client_addr.sin_addr.s_addr ) )
@@ -264,9 +294,10 @@ void Server_Start(void)
                                break;
                        }
                }
+               #endif
                
                // TODO: Multithread this?
-               Server_HandleClient(client_socket, bTrusted);
+               Server_HandleClient(client_socket, bTrusted, bRootPort);
                
                close(client_socket);
        }
@@ -284,7 +315,7 @@ void Server_Cleanup(void)
  * \param Socket       Client socket number/handle
  * \param bTrusted     Is the client trusted?
  */
-void Server_HandleClient(int Socket, int bTrusted)
+void Server_HandleClient(int Socket, int bTrusted, int bRootPort)
 {
        char    inbuf[INPUT_BUFFER_SIZE];
        char    *buf = inbuf;
@@ -297,7 +328,8 @@ void Server_HandleClient(int Socket, int bTrusted)
        // Initialise Client info
        clientInfo.Socket = Socket;
        clientInfo.ID = giServer_NextClientID ++;
-       clientInfo.bIsTrusted = bTrusted;
+       clientInfo.bTrustedHost = bTrusted;
+       clientInfo.bCanAutoAuth = bTrusted && bRootPort;
        clientInfo.EffectiveUID = -1;
        
        // Read from client
@@ -492,7 +524,7 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
        }
        
        // Check if trusted
-       if( !Client->bIsTrusted ) {
+       if( !Client->bCanAutoAuth ) {
                if(giDebugLevel)
                        Debug(Client, "Untrusted client attempting to AUTOAUTH");
                sendf(Client->Socket, "401 Untrusted\n");
@@ -548,35 +580,20 @@ void Server_Cmd_AUTHIDENT(tClient *Client, char *Args)
        char    *username;
         int    userflags;
        const int ident_timeout = 5;
-       socklen_t len;
-       struct sockaddr_in client_addr;
-       uint32_t  client_ip;
 
        if( Args != NULL && strlen(Args) ) {
                sendf(Client->Socket, "407 AUTHIDENT takes no arguments\n");
                return ;
        }
 
-       // Check if trusted (only works with INET sockets at present)
-       len = sizeof(client_addr);
-       if( getpeername(Client->Socket, (struct sockaddr*)&client_addr, &len) == -1 ) {
-               Debug(Client, "500 getpeername() failed\n");
-               perror("Getting AUTHIDENT peer name");
-               sendf(Client->Socket, "500 getpeername() failed\n");
+       // Check if trusted
+       if( !Client->bTrustedHost ) {
+               if(giDebugLevel)
+                       Debug(Client, "Untrusted client attempting to AUTHIDENT");
+               sendf(Client->Socket, "401 Untrusted\n");
                return ;
        }
 
-       client_ip = client_addr.sin_addr.s_addr;
-       if(giDebugLevel >= 2) {
-               Debug(Client, "client_ip = %x, ntohl(client_ip) = %x", client_ip, ntohl(client_ip));
-       }
-       if( ntohl(client_ip) != 0x7F000001 && (ntohl(client_ip) & IDENT_TRUSTED_NETMASK) != IDENT_TRUSTED_NETWORK ) {
-                       if(giDebugLevel)
-                               Debug(Client, "Untrusted client attempting to AUTHIDENT");
-                       sendf(Client->Socket, "401 Untrusted\n");
-                       return ;
-       }
-
        // Get username via IDENT
        username = ident_id(Client->Socket, ident_timeout);
        if( !username ) {

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