/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 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 "./aqhomestorage_p.h" #include "aqhome/msg/endpoint_tty.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/mqtt/endpoint_mqttc.h" #include "aqhome/http/endpoint_http.h" #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ */ //#define I18N(msg) msg #define I18S(msg) msg /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static void _setupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static void _setupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs); static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); static GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); static int _createPidFile(const char *pidFilename); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv) { GWEN_DB_NODE *dbArgs; int rv; 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->dbArgs=dbArgs; s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_STORAGE_DEFAULT_PIDFILE); if (s && *s) { AqHomeStorage_SetPidFile(aqh, s); rv=_createPidFile(s); if (rv<0) { DBG_ERROR(NULL, "Error creating PID file (%d)", rv); return rv; } } // _setupStorage(aqh, dbArgs); _setupIpc(aqh, dbArgs); _setupMqtt(aqh, dbArgs); _setupHttp(aqh, dbArgs); return 0; } int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) { AQH_STORAGE *sto; const char *stateFile; stateFile=GWEN_DB_GetCharValue(dbArgs, "stateFile", 0, NULL); sto=AQH_Storage_new(); aqh->storage=sto; return 0; } void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) { const char *tcpAddress; int tcpPort; tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL); tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, AQHOME_STORAGE_DEFAULT_IPC_PORT); if (tcpAddress && *tcpAddress && tcpPort) { GWEN_MSG_ENDPOINT *ep; ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh); GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); aqh->ipcdEndpoint=ep; } } void _setupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) { const char *mqttAddress; int mqttPort; const char *mqttClientId; int mqttKeepAlive; mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL); mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, AQHOME_STORAGE_DEFAULT_MQTT_PORT); mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, AQHOME_STORAGE_DEFAULT_MQTT_CLIENTID); mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE); if (mqttAddress && *mqttAddress && mqttPort) { GWEN_MSG_ENDPOINT *ep; int rv; ep=AQH_MqttClientEndpoint_new(mqttClientId, mqttAddress, mqttPort, NULL, 0); AQH_MqttClientEndpoint_SetKeepAliveTime(ep, mqttKeepAlive); GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); aqh->mqttEndpoint=ep; rv=AQH_MqttClientEndpoint_StartConnect(ep); if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) { DBG_ERROR(NULL, "Error connecting to MQTT server %s:%d (%d), will retry later", mqttAddress, mqttPort, rv); } } } void _setupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) { const char *tcpAddress; int tcpPort; tcpAddress=GWEN_DB_GetCharValue(dbArgs, "httpAddress", 0, NULL); tcpPort=GWEN_DB_GetIntValue(dbArgs, "httpPort", 0, AQHOME_STORAGE_DEFAULT_HTTP_PORT); if (tcpAddress && *tcpAddress && tcpPort) { GWEN_MSG_ENDPOINT *ep; ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptHttpFn, aqh); GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep); aqh->httpdEndpoint=ep; } } GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, GWEN_UNUSED void *data) { /* AQHOME_STORAGE *aqh; * * aqh=(AQHOME_STORAGE*) data; */ DBG_INFO(NULL, "Incoming IPC connection"); return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, 0); } GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, GWEN_UNUSED void *data) { GWEN_MSG_ENDPOINT *epIncoming; /* AQHOME_STORAGE *aqh; * * aqh=(AQHOME_STORAGE*) data; */ DBG_INFO(NULL, "Incoming HTTP connection"); epIncoming=GWEN_MsgEndpoint_new("http", 0); GWEN_MsgEndpoint_SetSocket(epIncoming, sk); GWEN_MsgIoEndpoint_Extend(epIncoming); AQH_HttpEndpoint_Extend(epIncoming, AQH_ENDPOINT_HTTP_FLAGS_PASSIVE); return epIncoming; } 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 _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 */ "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 */ "tcpAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "t", /* short option */ "tcpaddress", /* long option */ I18S("Specify the TCP address to listen on (disabled if missing)"), I18S("Specify the TCP address to listen on (disabled if missing)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "tcpPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "P", /* short option */ "tcpport", /* long option */ I18S("Specify the TCP port to listen on"), I18S("Specify the TCP port to listen on") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "mqttAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "ma", /* short option */ "mqttaddress", /* long option */ I18S("Specify the address of the MQTT server to connect to (disabled if missing)"), I18S("Specify the address of the MQTT server to connect to (disabled if missing)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "mqttPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "mp", /* short option */ "mqttport", /* long option */ I18S("Specify the port of the MQTT server (default: 1883)"), I18S("Specify the port of the MQTT server (default: 1883)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "mqttClientId", /* name */ 0, /* minnum */ 1, /* maxnum */ NULL, /* short option */ "mqttclientid", /* long option */ I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")"), I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "mqttKeepAlive", /* name */ 0, /* minnum */ 1, /* maxnum */ "mk", /* short option */ "mqttkeepalive", /* long option */ I18S("Specify keepalive time in seconds (defaults: 600)"), I18S("Specify keepalive time in seconds (defaults: 600)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "httpAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "ma", /* short option */ "httpaddress", /* long option */ I18S("Specify the address to bind the http service to (disabled if missing)"), I18S("Specify the address to bind the http service to (disabled if missing)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "httpPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "mp", /* short option */ "httpport", /* long option */ I18S("Specify the port to listen on for HTTP connections"), I18S("Specify the port to listen on for HTTP connections") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "statefile", /* name */ 0, /* minnum */ 1, /* maxnum */ "S", /* short option */ "statefile", /* long option */ I18S("File where rooms, devices and values etc. are stored"), I18S("File where rooms, devices and values etc. are stored") }, { 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_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; }