/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2024 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./init.h" #include "./net_read.h" #include "./aqhome_react_p.h" #include "aqhome-react/units/u_timer.h" #include "aqhome-react/units/u_varchanges.h" #include "aqhome/aqhome.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/ipc/endpoint_ipcclient.h" #include "aqhome/mqtt/endpoint_mqttc.h" #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ */ #define I18N(msg) msg #define I18S(msg) msg /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static int _createPidFile(const char *pidFilename); static int _setupBroker(AQHOME_REACT *aqh, GWEN_DB_NODE *dbArgs); static void _setupBuiltinUnits(AQHOME_REACT *aqh); static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv) { int rv; GWEN_DB_NODE *dbArgs; const char *s; dbArgs=GWEN_DB_Group_new("args"); rv=_readArgs(argc, argv, dbArgs); if (rv<0) { DBG_ERROR(NULL, "Error reading args (%d)", rv); return rv; } AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile"); aqh->dbArgs=dbArgs; s=GWEN_DB_GetCharValue(dbArgs, "loglevel", 0, NULL); if (s && *s) { GWEN_LOGGER_LEVEL ll; ll=GWEN_Logger_Name2Level(s); GWEN_Logger_SetLevel(NULL, ll); } aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_REACT_DEFAULT_PIDFILE); if (s && *s) { AqHomeReact_SetPidFile(aqh, s); rv=_createPidFile(s); if (rv<0) { DBG_ERROR(NULL, "Error creating PID file (%d)", rv); return rv; } } AqHomeReact_ReloadUnitNets(aqh); rv=_setupBroker(aqh, dbArgs); if (rv<0) { DBG_ERROR(NULL, "Error setting up connection to broker (%d)", rv); return rv; } return 0; } void AqHomeReact_ReloadUnitNets(AQHOME_REACT *aqh) { AQHREACT_UNIT_NET_LIST *unitNetList; AQHREACT_UnitNet_List_Clear(aqh->unitNetList); AQHREACT_Unit_List_Clear(aqh->unitList); aqh->timerUnit=NULL; aqh->varChangeUnit=NULL; _setupBuiltinUnits(aqh); unitNetList=AQHomeReact_ReadUnitNetFiles(aqh); if (unitNetList) { AQHREACT_UnitNet_List_free(aqh->unitNetList); aqh->unitNetList=unitNetList; { GWEN_BUFFER *dbuf; dbuf=GWEN_Buffer_new(0, 256, 0, 1); AQHREACT_UnitNet_List_Dump(unitNetList, dbuf, 2, "Loaded networks:"); fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(dbuf)); GWEN_Buffer_free(dbuf); } } else { DBG_INFO(NULL, "No unit nets read"); } } int _createPidFile(const char *pidFilename) { FILE *f; int pidfd; if (remove(pidFilename)==0) { DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)"); } #ifdef HAVE_SYS_STAT_H pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (pidfd < 0) { DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); return GWEN_ERROR_IO; } f = fdopen(pidfd, "w"); #else /* HAVE_STAT_H */ f=fopen(pidFilename,"w+"); #endif /* HAVE_STAT_H */ /* write pid */ #ifdef HAVE_GETPID fprintf(f,"%d\n",getpid()); #else fprintf(f,"-1\n"); #endif if (fclose(f)) { DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); return GWEN_ERROR_IO; } return 0; } int _setupBroker(AQHOME_REACT *aqh, GWEN_DB_NODE *dbArgs) { const char *brokerAddress; int brokerPort; const char *brokerClientId; brokerAddress=GWEN_DB_GetCharValue(dbArgs, "brokerAddress", 0, NULL); if (!(brokerAddress && *brokerAddress)) brokerAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, "127.0.0.1"); brokerPort=GWEN_DB_GetIntValue(dbArgs, "brokerPort", 0, -1); if (brokerPort<0) brokerPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOME_REACT_DEFAULT_BROKER_PORT); brokerClientId=GWEN_DB_GetCharValue(dbArgs, "brokerClientId", 0, AQHOME_REACT_DEFAULT_BROKER_CLIENTID); if (brokerAddress && *brokerAddress && brokerPort) { GWEN_MSG_ENDPOINT *ep; GWEN_MSG_ENDPOINT *ipcBaseEndpoint; int rv; ep=AQH_ClientIpcEndpoint_new("brokerIpcClient", 0); GWEN_MsgEndpoint_AddFlags(ep, AQH_IPCENDPOINT_FLAGS_WANTUPDATES); ipcBaseEndpoint=AQH_IpcEndpoint_CreateIpcTcpClient(brokerAddress, brokerPort, "brokerPhysEndpoint", 0); AQH_IpcEndpoint_SetServiceName(ipcBaseEndpoint, brokerClientId); GWEN_MsgEndpoint_Tree2_AddChild(ep, ipcBaseEndpoint); aqh->brokerEndpoint=ep; rv=GWEN_MultilayerEndpoint_StartConnect(ep); if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) { DBG_ERROR(NULL, "Error connecting to broker server %s:%d (%d), will retry later", brokerAddress, brokerPort, rv); return rv; } } return 0; } void _setupBuiltinUnits(AQHOME_REACT *aqh) { AQHREACT_UNIT *unit; unit=AqHomeReact_UnitTimer_new(aqh); AQHREACT_Unit_SetId(unit, ".timer"); AQHREACT_Unit_List_Add(unit, aqh->unitList); aqh->timerUnit=unit; unit=AqHomeReact_UnitVarChanges_new(aqh); AQHREACT_Unit_SetId(unit, ".updatedValue"); AQHREACT_Unit_List_Add(unit, aqh->unitList); aqh->varChangeUnit=unit; } int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) { int rv; const GWEN_ARGS args[]= { { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "loglevel", /* name */ 0, /* minnum */ 1, /* maxnum */ "L", /* short option */ "loglevel", /* long option */ I18S("Specify loglevel"), /* short description */ I18S("Specify loglevel") /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "cfgdir", /* name */ 0, /* minnum */ 1, /* maxnum */ "D", /* short option */ "cfgdir", /* long option */ I18S("Specify the configuration folder"), I18S("Specify the configuration folder") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "charset", /* name */ 0, /* minnum */ 1, /* maxnum */ 0, /* short option */ "charset", /* long option */ I18S("Specify the output character set"), /* short description */ I18S("Specify the output character set") /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "brokerAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "ba", /* short option */ "brokeraddress", /* long option */ I18S("Specify the address of the broker server to connect to (disabled if missing)"), I18S("Specify the address of the broker server to connect to (disabled if missing)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "brokerPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "bp", /* short option */ "brokerport", /* long option */ I18S("Specify the port of the broker server (default: 1899)"), I18S("Specify the port of the broker server (default: 1899)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "brokerClientId", /* name */ 0, /* minnum */ 1, /* maxnum */ NULL, /* short option */ "brokerclientid", /* long option */ I18S("Specify client id for the broker server (default: \"nodes\")"), I18S("Specify client id for the broker server (default: \"nodes\")") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "pidfile", /* name */ 0, /* minnum */ 1, /* maxnum */ "p", /* short option */ "pidfile", /* long option */ I18S("Specify the PID file"), I18S("Specify the PID file") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "timeout", /* name */ 0, /* minnum */ 1, /* maxnum */ "T", /* short option */ "timeout", /* long option */ I18S("Specify timeout in second (default: no timeout)"), I18S("Specify timeout in second (default: no timeout)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "devicefile", /* name */ 0, /* minnum */ 1, /* maxnum */ "d", /* short option */ "devicefile", /* long option */ I18S("Specify the device file"), I18S("Specify the device file") }, { GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ GWEN_ArgsType_Int, /* type */ "help", /* name */ 0, /* minnum */ 0, /* maxnum */ "h", /* short option */ "help", I18S("Show this help screen."), I18S("Show this help screen.") } }; rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs); if (rv==GWEN_ARGS_RESULT_ERROR) { fprintf(stderr, "ERROR: Could not parse arguments main\n"); return GWEN_ERROR_INVALID; } else if (rv==GWEN_ARGS_RESULT_HELP) { GWEN_BUFFER *ubuf; ubuf=GWEN_Buffer_new(0, 1024, 0, 1); GWEN_Buffer_AppendArgs(ubuf, I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"), AQHOME_VERSION_STRING, argv[0]); if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { fprintf(stderr, "ERROR: Could not create help string\n"); return 1; } GWEN_Buffer_AppendString(ubuf, "\n"); fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf)); GWEN_Buffer_free(ubuf); return GWEN_ERROR_CLOSE; } return 0; }