Merge branch 'master' of github.com:ucc/OpenDispense2
authorJohn Hodge <[email protected]>
Thu, 16 Apr 2015 06:18:37 +0000 (14:18 +0800)
committerJohn Hodge <[email protected]>
Thu, 16 Apr 2015 06:18:37 +0000 (14:18 +0800)
BuildAndPush
client.conf
src/client/main.c
src/common/config.c
src/common/config.h
src/server/common.h
src/server/handler_coke.c
src/server/handler_door.c
src/server/logging.c
src/server/main.c
src/server/server.c

index f0cd247..bd27baf 100755 (executable)
@@ -5,8 +5,10 @@
 set -o errexit
 set -o nounset
 CLIENT_PATH=/usr/local/bin/dispense
-SETPERMS="true"
-#SETPERMS="chown root:root $CLIENT_PATH; chmod u+s $CLIENT_PATH"
+EXTRA="true"
+EXTRA=$EXTRA";mkdir -p /etc/opendispense/"
+EXTRA=$EXTRA";cp ~tpg/gitclones/opendispense2/client.cfg /etc/opendispense/client.cfg"
+#EXTRA="chown root:root $CLIENT_PATH; chmod u+s $CLIENT_PATH"
 MAKEMAN="make -C ~tpg/gitclones/opendispense2/docs/"
 
 cd ~/gitclones/opendispense2
@@ -26,15 +28,15 @@ cp ~/gitclones/opendispense2/dispense /away/wheel/tpg/dispense_64bit
 cp /usr/share/man/man1/dispense.1.gz /away/wheel/tpg/dispense.1.gz
 
 # Copy 32-bit
-$SSH root@mussel "cp ~tpg/gitclones/opendispense2_32bit/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
-$SSH root@mooneye "cp ~tpg/gitclones/opendispense2_32bit/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
+$SSH root@mussel "cp ~tpg/gitclones/opendispense2_32bit/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
+$SSH root@mooneye "cp ~tpg/gitclones/opendispense2_32bit/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
 
 # Copy 64-bit
-$SSH root@motsugo "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
-$SSH root@mantis "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
-#$SSH root@martello "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
-$SSH root@merlo "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $SETPERMS; $MAKEMAN"
+$SSH root@motsugo "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
+#$SSH root@mantis "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
+#$SSH root@martello "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
+$SSH root@merlo "cp ~tpg/gitclones/opendispense2/dispense $CLIENT_PATH; $EXTRA; $MAKEMAN"
 
 # Copy Away version
-$SSH root@meersau "cp ~tpg/dispense_32bit $CLIENT_PATH; $SETPERMS; cp ~tpg/dispense.1.gz /usr/share/man/man1/dispense.1.gz"
+$SSH root@meersau "cp ~tpg/dispense_32bit $CLIENT_PATH; cp ~tpg/dispense.1.gz /usr/share/man/man1/dispense.1.gz"
 
index ff657ee..3f374ab 100644 (file)
@@ -2,4 +2,4 @@
 # OpenDispense2 Client config file
 #
 dispense_server merlo.ucc.asn.au
-dispense_port 11021
+dispense_port 11020
index ede9290..3c017a4 100644 (file)
@@ -618,14 +618,17 @@ int main(int argc, char *argv[])
                return ret;
 
        // Load config file
-       Config_ParseFile(gsConfigFile);
+       // - Don't check return value, leads to defaults being used
+       if( Config_ParseFile(gsConfigFile) ) {
+               fprintf(stderr, "NOTICE: Loading of config file '%s' failed, using defaults\n", gsConfigFile);
+       }
 
        // Parse config values
-       if (!giDispenseServerSet) {
-               gsDispenseServer        = Config_GetValue("dispense_server",0);
+       if( !giDispenseServerSet ) {
+               Config_GetValue_Str("dispense_server", &gsDispenseServer);
        }
        if (!giDispensePortSet) {
-               giDispensePort          = Config_GetValue_Int("dispense_port",0);
+               Config_GetValue_Int("dispense_port", &giDispensePort);
        }
 
 
@@ -844,11 +847,25 @@ int main(int argc, char *argv[])
 
 int ParseArguments(int argc, char *argv[])
 {
+       bool    rest_free = false;
        for( int i = 1; i < argc; i ++ )
        {
                char    *arg = argv[i];
                
-               if( arg[0] == '-' )
+               // If it doesn't start with a '-', or -- has been seen
+               // XXX: Hack - If parsing "user type", don't parse - options 
+               bool hack_usertype = (i > 2 && strcmp(argv[i-2], "user") == 0 && strcmp(argv[i-1], "type") == 0);
+               if( rest_free || arg[0] != '-' || hack_usertype )
+               {
+                       if( giTextArgc == MAX_TXT_ARGS )
+                       {
+                               fprintf(stderr, "ERROR: Too many arguments\n");
+                               return RV_ARGUMENTS;
+                       }
+               
+                       gsTextArgs[giTextArgc++] = argv[i];
+               }
+               else if( arg[1] != '-' )
                {                       
                        switch(arg[1])
                        {
@@ -858,8 +875,6 @@ int ParseArguments(int argc, char *argv[])
                                exit(0);
                                        
                        case 'c':
-                               if( i > 2 && strcmp(argv[i-1], "type") == 0 )
-                                       goto _default;
                                if( i + 1 >= argc ) {
                                        fprintf(stderr, "%s: -c takes an argument\n", argv[0]);
                                        ShowUsage();
@@ -947,54 +962,47 @@ int ParseArguments(int argc, char *argv[])
                        case 'n':       // Dry Run / read-only
                                gbDryRun = 1;
                                break;
-                       case '-':
-                               if( strcmp(argv[i], "--help") == 0 ) {
-                                       ShowUsage();
-                                       exit(0);
-                               }
-                               else if( strcmp(argv[i], "--dry-run") == 0 ) {
-                                       gbDryRun = 1;
-                               }
-                               else if( strcmp(argv[i], "--drinks-only") == 0 ) {
-                                       giUIMode = UI_MODE_DRINKSONLY;
-                               }
-                               else if( strcmp(argv[i], "--can-select-all") == 0 ) {
-                                       gbDisallowSelectWithoutBalance = 0;
-                               }
-                               else {
-                                       fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]);
-                                       ShowUsage();
-                                       return RV_ARGUMENTS;
-                               }
-                               break;
-                       default: _default:
-                               // The first argument is not allowed to begin with 'i'
-                               // (catches most bad flags)
-                               if( giTextArgc == 0 ) {
-                                       fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]);
-                                       ShowUsage();
-                                       return RV_ARGUMENTS;
-                               }
-                               if( giTextArgc == MAX_TXT_ARGS )
-                               {
-                                       fprintf(stderr, "ERROR: Too many arguments\n");
-                                       return RV_ARGUMENTS;
-                               }
-                               gsTextArgs[giTextArgc++] = argv[i];
-                               break;
+                       default:
+                               fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]);
+                               ShowUsage();
+                               return RV_ARGUMENTS;
                        }
 
                        continue;
                }
-
-               if( giTextArgc == MAX_TXT_ARGS )
+               else
                {
-                       fprintf(stderr, "ERROR: Too many arguments\n");
-                       return RV_ARGUMENTS;
+                       // '--' : Terminate argument processing (remainder is free)
+                       if( arg[2] == '\0' ) {
+                               rest_free = true;
+                       }
+                       else if( strcmp(arg, "--help") == 0 ) {
+                               ShowUsage();
+                               exit(0);
+                       }
+                       else if( strcmp(arg, "--dry-run") == 0 ) {
+                               gbDryRun = 1;
+                       }
+                       else if( strcmp(arg, "--drinks-only") == 0 ) {
+                               giUIMode = UI_MODE_DRINKSONLY;
+                       }
+                       else if( strcmp(arg, "--can-select-all") == 0 ) {
+                               gbDisallowSelectWithoutBalance = 0;
+                       }
+                       else if( strcmp(arg, "--configfile") == 0 ) {
+                               if( i + 1 >= argc ) {
+                                       fprintf(stderr, "%s: %s takes an argument\n", argv[0], arg);
+                                       ShowUsage();
+                                       return RV_ARGUMENTS;
+                               }
+                               gsConfigFile = argv[++i];
+                       }
+                       else {
+                               fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], arg);
+                               ShowUsage();
+                               return RV_ARGUMENTS;
+                       }
                }
-       
-               gsTextArgs[giTextArgc++] = argv[i];
-       
        }
        return 0;
 }
@@ -1004,18 +1012,13 @@ int ParseArguments(int argc, char *argv[])
 // ---------------
 char *trim(char *string)
 {
-        int    i;
-       
+       // Increment pointer while it points to a space
        while( isspace(*string) )
                string ++;
        
-       for( i = strlen(string); i--; )
-       {
-               if( isspace(string[i]) )
-                       string[i] = '\0';
-               else
-                       break;
-       }
+       // And repalce trailing spaces with NUL bytes
+       for( int i = strlen(string); i-- && isspace(string[i]); )
+               string[i] = '\0';
        
        return string;
 }
index 563240a..ca6db69 100644 (file)
@@ -38,7 +38,7 @@ struct sConfigKey
 };
 
 // === PROTOTYPES ===
-void   Config_ParseFile(const char *Filename);
+//int  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);
@@ -49,9 +49,8 @@ const char    *Config_GetValue(const char *KeyName, int Index);
 tConfigKey     *gConfig;
 
 // === CODE ===
-void Config_ParseFile(const char *Filename)
+int Config_ParseFile(const char *Filename)
 {
-       FILE    *fp;
        char    line[MAX_LINE_LEN];
        regex_t regexp_option;
        regex_t regexp_empty;
@@ -59,11 +58,11 @@ void Config_ParseFile(const char *Filename)
        CompileRegex(&regexp_option, "^\\s*([^ \t]+)\\s+(.+)$", REG_EXTENDED);  //
        CompileRegex(&regexp_empty, "^\\s*$", REG_EXTENDED);    //
        
-       fp = fopen(Filename, "r");
+       FILE *fp = fopen(Filename, "r");
        if(!fp) {
                fprintf(stderr, "Unable to open config file '%s'\n", Filename);
                perror("Config_ParseFile");
-               exit(-1);
+               return 1;
        }
        
        while( fgets(line, sizeof(line), fp) )
@@ -104,6 +103,8 @@ void Config_ParseFile(const char *Filename)
        fclose(fp);
        regfree(&regexp_option);
        regfree(&regexp_empty);
+
+       return 0;
 }
 
 void Config_AddValue(const char *Key, const char *Value)
@@ -191,12 +192,11 @@ int Config_GetValueCount(const char *KeyName)
        return key->ValueCount;
 }
 
-const char *Config_GetValue(const char *KeyName, int Index)
+const tConfigValue *Config_int_GetValue(const char *KeyName, int Index)
 {
-       tConfigKey      *key;
        tConfigValue    *val;   
 
-       key = Config_int_GetKey(KeyName, 0);
+       tConfigKey *key = Config_int_GetKey(KeyName, 0);
        if(!key) {
                fprintf(stderr, "Unknown key '%s'\n", KeyName);
                exit(1);
@@ -208,15 +208,25 @@ const char *Config_GetValue(const char *KeyName, int Index)
        for( val = key->FirstValue; Index && val; val = val->Next, Index -- );
 
        ASSERT(val != NULL);
-       
+       return val;
+}
+
+const char *Config_GetValue_Idx(const char *KeyName, int Index)
+{
+       const tConfigValue* val = Config_int_GetValue(KeyName, Index);
+       if( !val )      return NULL;
        return val->Data;
 }
 
-int Config_GetValue_Bool(const char *KeyName, int Index)
+bool Config_GetValue_Str(const char *KeyName, const char** Value)
+{
+       const tConfigValue* val = Config_int_GetValue(KeyName, 0);
+       if(!val)        return false;
+       *Value = val->Data;
+       return true;
+}
+bool str_to_bool(const char *val)
 {
-       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;
        
@@ -225,19 +235,32 @@ int Config_GetValue_Bool(const char *KeyName, int Index)
        
        if( strcasecmp(val, "yes") == 0 )       return 1;
        if( strcasecmp(val, "no") == 0 )        return 0;
+
+       // INVALID, TODO: Error message
+       return 0;
+}
+bool Config_GetValue_Bool(const char *KeyName, bool* Value)
+{
+       const tConfigValue* val = Config_int_GetValue(KeyName, 0);
+       if(!val)        return false;
        
-       return -1;
+       *Value = str_to_bool(val->Data);
+       
+       return true;
 }
 
-int Config_GetValue_Int(const char *KeyName, int Index)
+bool Config_GetValue_Int(const char *KeyName, int* Value)
 {
-        int    tmp;
-       const char *val = Config_GetValue(KeyName, Index);
-       if(!val)        return -1;
+       const tConfigValue* val = Config_int_GetValue(KeyName, 0);
+       if(!val)        return false;
        
-       if( (tmp = atoi(val)) ) return tmp;
-       if( val[0] == '0' && val[1] == '\0' )   return 0;
+       char* end;
+        int    value = strtol(val->Data, &end, 0);
+       if( *end != '\0' ) {
+               return false;
+       }
+       *Value = value;
        
-       return -1;
+       return true;
 }
 
index 901cad8..34d7dc6 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef _CONFIG_H_
 #define _CONFIG_H_
 
+#include <stdbool.h>   // Because C
+
 // === HELPER MACROS ===
 #define _EXPSTR(x)      #x
 #define EXPSTR(x)       _EXPSTR(x)
 
 
 // --- Config Database ---
-extern void    Config_ParseFile(const char *Filename);
+extern int     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);
+extern const char      *Config_GetValue_Idx(const char *KeyName, int Index);
+
+extern bool    Config_GetValue_Str(const char *KeyName, const char** ValPtr);
+extern bool    Config_GetValue_Bool(const char *KeyName, bool* ValPtr);
+extern bool    Config_GetValue_Int(const char *KeyName, int* ValPtr);
 
 #endif
index 1bdca84..f1304db 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef _COMMON_H_
 #define _COMMON_H_
 
+#include <stdbool.h>   // Because C
+
 #include <regex.h>
 #include "../cokebank.h"
 
@@ -69,7 +71,9 @@ extern int    giNumItems;
 extern tHandler        *gaHandlers[];
 extern int     giNumHandlers;
 extern int     giDebugLevel;
-extern int     gbNoCostMode;
+extern bool    gbNoCostMode;
+
+extern bool    gbSyslogDisabled;
 
 // === FUNCTIONS ===
 extern void    Items_UpdateFile(void);
index a52e373..b499387 100644 (file)
@@ -53,7 +53,7 @@ const char    *gsCoke_ModbusAddress = "130.95.13.73";
 modbus_t       *gCoke_Modbus;
 time_t gtCoke_LastDispenseTime;
 time_t gtCoke_LastReconnectTime;
- int   gbCoke_DummyMode = 1;
+bool   gbCoke_DummyMode = 1;
  int   giCoke_NextCokeSlot = 0;
 
 // == CODE ===
@@ -61,11 +61,7 @@ int Coke_InitHandler()
 {
        // 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;
-       }
+       Config_GetValue_Bool("coke_dummy_mode", &gbCoke_DummyMode);
 
        // Open modbus
        if( !gbCoke_DummyMode )
index a6cc110..1db954f 100644 (file)
@@ -48,6 +48,7 @@ bool  gbDoor_LockThreadStarted;
 // === CODE ===
 void* Door_Lock(void* Unused __attribute__((unused)))
 {
+       gbDoor_LockThreadStarted = true;
        while(1)
        {
                sem_wait(&gDoor_UnlockSemaphore);
index dc3cc4c..2ef0e12 100644 (file)
 #include <syslog.h>
 
 // === GLOBALS ===
-bool   gbSyslogEnabled = true;
+bool   gbSyslogDisabled = true;
 
 // === CODE ==
 void Log_Error(const char *Format, ...)
 {
        va_list args;
 
-       va_start(args, Format);
-       if( gbSyslogEnabled )
+       if( !gbSyslogDisabled )
        {
+               va_start(args, Format);
                vsyslog(LOG_WARNING, Format, args);
+               va_end(args);
        }
-       else
-       {
-               fprintf(stderr, "WARNING: ");
-               vfprintf(stderr, Format, args);
-               fprintf(stderr, "\n");
-       }
+       
+       va_start(args, Format);
+       fprintf(stderr, "WARNING: ");
+       vfprintf(stderr, Format, args);
+       fprintf(stderr, "\n");
        va_end(args);
 }
 
@@ -36,17 +36,16 @@ void Log_Info(const char *Format, ...)
 {
        va_list args;
        
-       va_start(args, Format);
-       if( gbSyslogEnabled )
+       if( !gbSyslogDisabled )
        {
+               va_start(args, Format);
                vsyslog(LOG_INFO, Format, args);
+               va_end(args);
        }
-       else
-       {
-               fprintf(stderr, "WARNING: ");
-               vfprintf(stderr, Format, args);
-               fprintf(stderr, "\n");
-       }
+       va_start(args, Format);
+       fprintf(stderr, "INFO: ");
+       vfprintf(stderr, Format, args);
+       fprintf(stderr, "\n");
        va_end(args);
 }
 
index efa354e..2e1985d 100644 (file)
 extern void    Init_Handlers(void);
 extern void    Load_Itemlist(void);
 extern void    Server_Start(void);
-extern int     gbServer_RunInBackground;
+extern bool    gbServer_RunInBackground;
 extern int     giServer_Port;
 extern const char      *gsItemListFile;
 extern const char      *gsCoke_ModbusAddress;
 extern int     giCoke_ModbusPort;
 extern const char      *gsDoor_SerialPort;
-extern bool    gbSyslogEnabled;
 
 // === PROTOTYPES ===
 void   *Periodic_Thread(void *Unused);
 
 // === GLOBALS ===
  int   giDebugLevel = 0;
- int   gbNoCostMode = 0;
+bool   gbNoCostMode = 0;
 const char     *gsCokebankPath = "cokebank.db";
 // - Functions called every 20s (or so)
 #define ciMaxPeriodics 10
@@ -74,8 +73,16 @@ int main(int argc, char *argv[])
        for( i = 1; i < argc; i++ )
        {
                char    *arg = argv[i];
-               if( arg[0] == '-' && arg[1] != '-')
+               if( arg[0] != '-' )
                {
+                       // No free arguments please
+                       PrintUsage(argv[0]);
+                       return -1;
+               }
+               else if( arg[1] != '-' )
+               {
+                       // Single character arguments
+                       // - TODO: Process in a loop
                        switch(arg[1])
                        {
                        case 'f':
@@ -94,8 +101,9 @@ int main(int argc, char *argv[])
                                return -1;
                        }
                }
-               else if( arg[0] == '-' && arg[1] == '-' )
+               else
                {
+                       // Long arguments
                        if( strcmp(arg, "--configfile") == 0 ) {
                                if( i + 1 >= argc )     return -1;
                                config_file = argv[++i];
@@ -113,31 +121,41 @@ int main(int argc, char *argv[])
                                return -1;
                        }
                }
-               else
-               {
-                       // Usage Error
-                       PrintUsage(argv[0]);
-                       return -1;
-               }
        }
 
-       Config_ParseFile( config_file );
+       if( Config_ParseFile( config_file ) ) {
+               fprintf(stderr, "NOTICE: Loading of config file '%s' failed, using defaults\n", 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);
-       giServer_Port        = Config_GetValue_Int("server_port", 0);
-       gsItemListFile       = Config_GetValue("items_file", 0);
-
-       gbNoCostMode         = (Config_GetValue_Bool("test_mode", 0) == 1);
-       gbSyslogEnabled      = (Config_GetValue_Bool("disable_syslog", 0) == 0);
+       {
+               bool rv = true;
+               #define REQ_CFG(variable, type, name)   rv = Config_GetValue_##type(name, &variable) && rv
+               #define OPT_CFG(variable, type, name)        Config_GetValue_##type(name, &variable)
+               OPT_CFG(gbServer_RunInBackground, Bool, "daemonise");
+               OPT_CFG(giServer_Port, Int, "server_port");
+               
+               REQ_CFG(gsCokebankPath, Str, "cokebank_database");
+               REQ_CFG(gsItemListFile, Str, "items_file");
 
-       gsCoke_ModbusAddress = Config_GetValue("coke_modbus_address", 0);
-       giCoke_ModbusPort    = Config_GetValue_Int("coke_modbus_port", 0);
+               OPT_CFG(gsDoor_SerialPort, Str, "door_serial_port");
+               REQ_CFG(gsCoke_ModbusAddress, Str, "coke_modbus_address");
+               OPT_CFG(giCoke_ModbusPort,    Int,    "coke_modbus_port");
+               
+               OPT_CFG(gbNoCostMode,    Bool, "test_mode");
+               OPT_CFG(gbSyslogDisabled, Bool, "disable_syslog");
+               
+               if( !rv ) {
+                       fprintf(stderr, "ERROR: Some required configuration items were missing\n");
+                       return -1;
+               }
+       }
 
+       // - Cleanly tear down the server on SIGINT/SIGTERM
        signal(SIGINT, sigint_handler);
        signal(SIGTERM, sigint_handler);
+       // - ignore SIGPIPE to prevent a crashing client from bringing the server down too
+       signal(SIGPIPE, SIG_IGN);
        
        openlog("odispense2", 0, LOG_LOCAL4);
        
index 11846bb..b09c794 100644 (file)
@@ -148,7 +148,7 @@ void Server_Start(void)
        gaServer_TrustedHosts = malloc(giServer_NumTrustedHosts * sizeof(*gaServer_TrustedHosts));
        for( int i = 0; i < giServer_NumTrustedHosts; i ++ )
        {
-               const char      *addr = Config_GetValue("trusted_host", i);
+               const char      *addr = Config_GetValue_Idx("trusted_host", i);
                
                if( inet_aton(addr, &gaServer_TrustedHosts[i]) == 0 ) {
                        fprintf(stderr, "Invalid IP address '%s'\n", addr);

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