commit 5aae9c2cce6a1d84c484fb65cf0eb0fa85713735 Author: Carsten Larsen Date: Sat Nov 24 22:39:18 2018 +0100 Version 1.05 diff --git a/AmiTimeKeeper.i386-aros.readme b/AmiTimeKeeper.i386-aros.readme new file mode 100644 index 0000000..991cea2 --- /dev/null +++ b/AmiTimeKeeper.i386-aros.readme @@ -0,0 +1,25 @@ +Short: Keep your time right +Uploader: Carsten Larsen +Author: Carsten Larsen (carsten.larsen@mail.com) +Type: util/cdity +Version: 1.05 +Architecture: i386-aros + +AmiTimeKeeper is a small program which keeps the time right on your +machine. No installation is required. Only an internet connection is +needed. + +AmiTimeKeeper can be controlled through a number of settings. This +can be done either directly from the command line or by using the +tooltypes of an icon. Preferences can also be saved between reboots. + +The default server address is currently pool.ntp.org. To find another +server try visiting support.ntp.org or one of the other sites listing +time servers. + +Major changes in 1.05 06.08.2018 +- Preferences can now be saved from settings window +- Running multiple instances no longer creates zombie processes +- Low water option renamed to threshold and high water option removed +- New option for sending log messages to file instead of to screen +- Read-only options is now applied consistently diff --git a/AmiTimeKeeper.kdev4 b/AmiTimeKeeper.kdev4 new file mode 100644 index 0000000..509d95e --- /dev/null +++ b/AmiTimeKeeper.kdev4 @@ -0,0 +1,4 @@ +[Project] +CreatedFrom=Makefile.m68k +Manager=KDevCustomMakeManager +Name=AmiTimeKeeper diff --git a/AmiTimeKeeper.readme b/AmiTimeKeeper.readme new file mode 100644 index 0000000..5e20326 --- /dev/null +++ b/AmiTimeKeeper.readme @@ -0,0 +1,25 @@ +Short: Keep your time right +Uploader: Carsten Larsen +Author: Carsten Larsen (carsten.larsen@mail.com) +Type: util/cdity +Version: 1.05 +Architecture: m68k-amigaos + +AmiTimeKeeper is a small program which keeps the time right on your +machine. No installation is required. Only an internet connection is +needed. + +AmiTimeKeeper can be controlled through a number of settings. This +can be done either directly from the command line or by using the +tooltypes of an icon. Preferences can also be saved between reboots. + +The default server address is currently pool.ntp.org. To find another +server try visiting support.ntp.org or one of the other sites listing +time servers. + +Major changes in 1.05 06.08.2018 +- Preferences can now be saved from settings window +- Running multiple instances no longer creates zombie processes +- Low water option renamed to threshold and high water option removed +- New option for sending log messages to file instead of to screen +- Read-only options is now applied consistently diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..704f5fa --- /dev/null +++ b/ChangeLog @@ -0,0 +1,33 @@ +AmiTimeKeeper Change Log + +v1.05 06.08.2018 + - Preferences can now be saved from settings window + - Time zone is shown as UTC offset in settings window + - Running multiple instances no longer creates zombie processes + - Low water option renamed to threshold and high water option removed + - New option for sending log messages to file instead of to screen + - Some incorrect NTP log messages no longer appears + - Read-only options is now applied consistently + - Potential race condition in memory allocation fixed + - Other minor enhancements + +v1.04 15.12.2017 + - Default settings adjusted to comply better with AmigaOS + - Bugs in threshold settings (high/low water) fixed + - Bugs in opening and closing of libraries fixed + - New and more accurate error and log messages + +v1.03 09.12.2017 + - Settings window + +v1.02 29.11.2017 + - AROS code compliance + +v1.01 28.11.2017 + - Handle network disconnection + - Fix infinity timeout problem + - Support hardware clock + - Improved error messages + +v1.00 26.11.2017 + - First public release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8841b7d --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2001, 02 Motoyuki Kasahara +Copyright (c) 2007 TANDBERG Telecom AS +Copyright (c) 2008-2009 Dag-Erling Smørgrav +Copyright (c) 2017-2018 Carsten Sonne Larsen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.aros b/Makefile.aros new file mode 100644 index 0000000..4ed896f --- /dev/null +++ b/Makefile.aros @@ -0,0 +1,49 @@ + +all: timekeeper + +CC = gcc +CFLAGS += -O2 -fno-stack-protector -DAROS -Wall +LDLIBS += + +broker.o: compiler.h config.h message.h win.h + +com.o: compiler.h config.h message.h state.h time.h sntp.h mem.h + +library.o: compiler.h config.h time.h mem.h + +log.o: compiler.h config.h log.h mem.h + +main.o: compiler.h config.h state.h time.h mem.h + +mem.o: compiler.h config.h mem.h + +message.o: compiler.h config.h message.h mem.h + +net_getaddrinfo.o: compiler.h mem.h config.h net_getaddrinfo.h + +net_poll.o: compiler.h config.h mem.h + +net.o: compiler.h config.h net_getaddrinfo.h message.h mem.h + +sntp.o: compiler.h config.h sntp.h time.h net_getaddrinfo.h message.h mem.h + +state.o: compiler.h config.h state.h mem.h + +string.o: compiler.h config.h string.h mem.h + +time.o: compiler.h config.h time.h mem.h + +win_main.o: compiler.h config.h message.h state.h time.h mem.h win.h + +win_gad.o: compiler.h config.h message.h state.h time.h mem.h win.h + +timekeeper: broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o sntp.o state.o time.o string.o win_main.o win_gad.o + ${CC} ${CFLAGS} ${LFLAGS} -s -o TimeKeeper \ + broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o sntp.o state.o time.o string.o win_main.o win_gad.o \ + ${LDLIBS} + +clean: + rm -f TimeKeeper broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o settings.o sntp.o state.o time.o string.o win_main.o win_gad.o TimeKeeper.map + +depend: + @echo Dependencies already done diff --git a/Makefile.m68k b/Makefile.m68k new file mode 100644 index 0000000..dd33844 --- /dev/null +++ b/Makefile.m68k @@ -0,0 +1,58 @@ + +all: timekeeper + +CC = /opt/m68k-amigaos/bin/m68k-amigaos-gcc + +# clib2 +#CFLAGS = -O2 -DAOS3 -Iinclude -mcrt=clib2 -m68020 -fbaserel32 -Wall +#LFALGS = -Wl,--cref,-M,-Map=TimeKeeper.map +#LDLIBS = -lnet + +# libnix +#CFLAGS = -O2 -DAOS3 -fomit-frame-pointer -fno-stack-check -Iinclude -noixemul -Wall +CFLAGS = -O2 -DAOS3 -fno-stack-check -Iinclude -noixemul -Wall +LFLAGS = -Wl,--cref,-M,-Map=TimeKeeper.map +LDLIBS = + +broker.o: compiler.h config.h message.h win.h + +com.o: compiler.h config.h message.h state.h time.h sntp.h mem.h + +library.o: compiler.h config.h time.h mem.h + +log.o: compiler.h config.h log.h mem.h + +main.o: compiler.h config.h state.h time.h mem.h + +mem.o: compiler.h config.h mem.h + +message.o: compiler.h config.h message.h mem.h + +net_getaddrinfo.o: compiler.h mem.h config.h net_getaddrinfo.h + +net_poll.o: compiler.h config.h mem.h + +net.o: compiler.h config.h net_getaddrinfo.h message.h mem.h + +sntp.o: compiler.h config.h sntp.h time.h net_getaddrinfo.h message.h mem.h + +state.o: compiler.h config.h state.h mem.h + +string.o: compiler.h config.h string.h mem.h + +time.o: compiler.h config.h time.h mem.h + +win_main.o: compiler.h config.h message.h state.h time.h mem.h win.h + +win_gad.o: compiler.h config.h message.h state.h time.h mem.h win.h + +timekeeper: broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o sntp.o state.o time.o string.o win_main.o win_gad.o + ${CC} ${CFLAGS} ${LFLAGS} -s -o TimeKeeper \ + broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o sntp.o state.o time.o string.o win_main.o win_gad.o \ + ${LDLIBS} + +clean: + rm -f TimeKeeper broker.o com.o library.o log.o main.o mem.o message.o net_getaddrinfo.o net_poll.o net.o settings.o sntp.o state.o time.o string.o win_main.o win_gad.o TimeKeeper.map + +depend: + @echo Dependencies already done diff --git a/broker.c b/broker.c new file mode 100644 index 0000000..ebe4333 --- /dev/null +++ b/broker.c @@ -0,0 +1,331 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "message.h" +#include "mem.h" +#include "win.h" + +static void ProcessMsg(void); + +static struct NewBroker newBroker = { + NB_VERSION, + (STRPTR)APP_SHORT_NAME, + (STRPTR)APP_TITLE_VERSION, + (STRPTR) "Synchronize Clock With Internet Servers", + NBU_UNIQUE | NBU_NOTIFY, + COF_SHOW_HIDE, + 0, 0, 0}; + +void StartBroker(void) +{ + LONG error; + struct Message *msg; + + Globals->Broker->BrokerPort = CreateMsgPort(); + if (Globals->Broker->BrokerPort == NULL) + { + LogError("Could not allocate broker port"); + return; + } + + Globals->Broker->UserPort = CreateMsgPort(); + if (Globals->Broker->UserPort == NULL) + { + LogError("Could not allocate broker user port"); + DeleteMsgPort(Globals->Broker->BrokerPort); + Globals->Broker->BrokerPort = NULL; + return; + } + + Globals->Broker->ReplyPort = CreateMsgPort(); + if (Globals->Broker->ReplyPort == NULL) + { + LogError("Could not allocate broker reply port"); + DeleteMsgPort(Globals->Broker->BrokerPort); + DeleteMsgPort(Globals->Broker->UserPort); + Globals->Broker->BrokerPort = NULL; + Globals->Broker->UserPort = NULL; + return; + } + + Globals->Broker->ShutdownSigBit = AllocSignal(-1); + if (Globals->Broker->ShutdownSigBit == -1) + { + SendErrorMessage("Could not allocate signal for broker"); + DeleteMsgPort(Globals->Broker->BrokerPort); + DeleteMsgPort(Globals->Broker->UserPort); + DeleteMsgPort(Globals->Broker->ReplyPort); + Globals->Broker->BrokerPort = NULL; + Globals->Broker->UserPort = NULL; + Globals->Broker->ReplyPort = NULL; + return; + } + + newBroker.nb_Port = Globals->Broker->BrokerPort; + newBroker.nb_Pri = Globals->Settings->Priority; + + Globals->Broker->Object = CxBroker(&newBroker, &error); + if (!Globals->Broker->Object) + { + switch (error) + { + case CBERR_SYSERR: + LogError("System problems (CBERR_SYSERR). Could not allocate broker object"); + break; + case CBERR_DUP: + LogWarning(APP_SHORT_NAME " already running"); + break; + default: + LogError("Could not allocate broker object (error code: %ld)", error); + break; + } + + DeleteMsgPort(Globals->Broker->BrokerPort); + DeleteMsgPort(Globals->Broker->UserPort); + DeleteMsgPort(Globals->Broker->ReplyPort); + Globals->Broker->BrokerPort = NULL; + Globals->Broker->UserPort = NULL; + Globals->Broker->ReplyPort = NULL; + Globals->Broker->Object = NULL; + return; + } + + Globals->Broker->Task = FindTask(NULL); + + StartCom(); + ProcessMsg(); + + while ((msg = GetMsg(Globals->Broker->BrokerPort))) + ReplyMsg(msg); + + while ((msg = GetMsg(Globals->Broker->UserPort))) + ReplyMsg(msg); + + while ((msg = GetMsg(Globals->Broker->ReplyPort))) + ReplyMsg(msg); + + DeleteCxObj(Globals->Broker->Object); + FreeSignal(Globals->Broker->ShutdownSigBit); + DeleteMsgPort(Globals->Broker->BrokerPort); + DeleteMsgPort(Globals->Broker->UserPort); + DeleteMsgPort(Globals->Broker->ReplyPort); + Globals->Broker->BrokerPort = NULL; + Globals->Broker->UserPort = NULL; + Globals->Broker->ReplyPort = NULL; + Globals->Broker->Object = NULL; +} + +void ShutdownBroker(void) +{ + Signal((struct Task *)Globals->Broker->Task, + (1 << Globals->Broker->ShutdownSigBit)); +} + +void SetBrokerPriority(int priority) +{ + SetCxObjPri(Globals->Broker->Object, priority); +} + +static void Activate(void) +{ + LogWarning("ACTIVE changed: 1 -> 0"); + ActivateCxObj(Globals->Broker->Object, 0); + Globals->Active = false; + StopComAsync(); + while (!CheckComClosed()) + Delay(1); +} + +static void Deactivate(void) +{ + LogWarning("ACTIVE changed: 0 -> 1"); + ActivateCxObj(Globals->Broker->Object, 1); + Globals->Active = true; + StartCom(); +} + +static void StopSubProcesses(void) +{ + Forbid(); + Globals->ShuttingDown = true; + Permit(); + + HideSettingWindow(); + while (!CheckSettingWindowClosed()) + Delay(10); + + StopComAsync(); + while (!CheckComClosed()) + Delay(10); +} + +static void ProcessMsg(void) +{ + bool run = true; + ULONG sigmask = SIGBREAKF_CTRL_C | + (1 << Globals->Broker->ShutdownSigBit) | + (1 << Globals->Broker->BrokerPort->mp_SigBit) | + (1 << Globals->Broker->UserPort->mp_SigBit) | + (1 << Globals->Broker->ReplyPort->mp_SigBit); + + ActivateCxObj(Globals->Broker->Object, 1); + Globals->Active = true; + + do + { + ULONG sigrcvd = Wait(sigmask); + + if (sigrcvd & (1 << Globals->Broker->BrokerPort->mp_SigBit)) + { + CxMsg *msg; + while ((msg = (CxMsg *)GetMsg(Globals->Broker->BrokerPort))) + { + LONG msgid = CxMsgID(msg); + ULONG msgtype = CxMsgType(msg); + ReplyMsg((struct Message *)msg); + + switch (msgtype) + { + case CXM_IEVENT: + break; + case CXM_COMMAND: + switch (msgid) + { + case CXCMD_DISABLE: + Activate(); + break; + case CXCMD_ENABLE: + Deactivate(); + break; + case CXCMD_KILL: + LogInfo("Received shut down from commodity"); + LogWarning("Shutting down"); + StopSubProcesses(); + run = false; + break; + case CXCMD_UNIQUE: + LogWarning("Commodity duplicate detected"); + LogTrace("Show window"); + ShowSettingWindow(); + break; + case CXCMD_APPEAR: + LogTrace("Show window"); + ShowSettingWindow(); + break; + case CXCMD_DISAPPEAR: + LogTrace("Hide window"); + HideSettingWindow(); + break; + } + } + } + } + + if (sigrcvd & (1 << Globals->Broker->UserPort->mp_SigBit)) + { + struct AppWindowMessage *msg; + while ((msg = (struct AppWindowMessage *)GetMsg(Globals->Broker->UserPort))) + { + switch (msg->Type) + { + case ATK_LOGERROR: + LogError(msg->Text); + break; + case ATK_LOGWARN: + LogWarning(msg->Text); + break; + case ATK_LOGINFO: + LogInfo(msg->Text); + break; + case ATK_LOGTRACE: + LogTrace(msg->Text); + break; + case ATK_STORE: + SaveSettings(true); + SaveSettings(false); + break; + case ATK_APPLY: + SaveSettings(false); + break; + case ATK_UNDO: + ApplyAppSettings(Globals->Window->Settings, true); + break; + case ATK_RESTART: + RestartSntpAsync(); + break; + case ATK_REFRESH: + // Broker cannot refresh + break; + case ATK_DISABLE: + Activate(); + break; + case ATK_ENABLE: + Deactivate(); + break; + case ATK_READONLY: + LogWarning("READONLY changed: 1 -> 0"); + break; + case ATK_READWRITE: + LogWarning("READONLY changed: 0 -> 1"); + break; + default: + break; + } + ReplyMsg((struct Message *)msg); + } + } + + if (sigrcvd & (1 << Globals->Broker->ReplyPort->mp_SigBit)) + { + struct AppWindowMessage *msg; + while ((msg = (struct AppWindowMessage *)GetMsg(Globals->Broker->ReplyPort))) + { + if (msg->Text != NULL) + { + FreeMemSafe(msg->Text); + } + FreeMemSafe(msg); + } + } + + if (sigrcvd & (1 << Globals->Broker->ShutdownSigBit)) + { + LogInfo("Received shut down from setting window"); + LogWarning("Shutting down"); + StopSubProcesses(); + run = false; + } + + if (sigrcvd & SIGBREAKF_CTRL_C) + { + LogInfo("Received CTRL + C"); + LogWarning("Shutting down"); + StopSubProcesses(); + run = false; + } + } while (run); +} diff --git a/com.c b/com.c new file mode 100644 index 0000000..590846c --- /dev/null +++ b/com.c @@ -0,0 +1,461 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "message.h" +#include "state.h" +#include "time.h" +#include "sntp.h" +#include "mem.h" + +struct AppCom +{ + bool Run; + bool Restart; + bool Exited; + struct Process *Task; + struct TimerInfo *TimerInfo; + struct sntp *Client; + short RestartSigBit; + bool FirstRun; + int RunCount; + int FailCount; +}; + +static void SyncProc(void); +static void ComOpen(void); +static void ComInit(void); +static int SyncClock(void); +static void ShowStats(void); +static ULONG GetSntpRestartSigMask(void); +static const char *SntpErrorText(enum sntp_err error); + +void RestartSntpAsync(void) +{ + if (Globals->Syncer->Task != NULL) + { + Signal((struct Task *)Globals->Syncer->Task, GetSntpRestartSigMask()); + } +} + +void StopComAsync(void) +{ + if (Globals->Syncer->Task != NULL) + { + Signal((struct Task *)Globals->Syncer->Task, SIGBREAKF_CTRL_C); + } +} + +bool CheckComClosed(void) +{ + return Globals->Syncer->Exited; +} + +void StartCom(void) +{ + if (Globals->Syncer != NULL && Globals->Syncer->Task != NULL) + { + LogTrace("Synchronizer process already running"); + return; + } + + if (Globals->Syncer == NULL) + { + Globals->Syncer = (struct AppCom *)AllocMemSafe(sizeof(struct AppCom)); + } + + Globals->Syncer->Run = true; + Globals->Syncer->Restart = false; + Globals->Syncer->Exited = false; + Globals->Syncer->FirstRun = true; + Globals->Syncer->RunCount = 0; + Globals->Syncer->FailCount = 0; + + Forbid(); + Globals->Syncer->Task = CreateNewProcTags( + NP_Entry, (IPTR)SyncProc, + NP_Name, (IPTR)APP_SHORT_NAME " Synchronizer", + NP_StackSize, 64 * 1024, + TAG_DONE); + Permit(); + + if (Globals->Syncer->Task != NULL) + { + LogTrace("Created synchronizer process"); + } + else + { + LogError("Failed to create synchronizer process"); + } +} + +static ULONG GetSntpRestartSigMask(void) +{ + return (1 << Globals->Syncer->RestartSigBit); +} + +static void ComDestroy(void) +{ + if (Globals->Syncer->Client != NULL) + { + sntp_destroy(Globals->Syncer->Client); + Globals->Syncer->Client = NULL; + } +} + +static void Sync(void) +{ + char message[SETTINGMESSAGELEN]; + ComOpen(); + SetTimer(Globals->Syncer->TimerInfo); + SNPrintf(message, SETTINGMESSAGELEN, + "Sending next NTP request in %ld milliseconds", + Globals->Settings->Interval); + SendTraceMessage(message); + ShowStats(); + Globals->Syncer->RunCount++; +} + +static void SyncProc(void) +{ + Globals->Syncer->TimerInfo = CreateTimer(false); + if (Globals->Syncer->TimerInfo == NULL) + { + SendErrorMessage("Could not create timer for synchronizer"); + Globals->Syncer->Exited = true; + return; + } + + Globals->Syncer->RestartSigBit = AllocSignal(-1); + if (Globals->Syncer->RestartSigBit == -1) + { + SendErrorMessage("Could not allocate signal for synchronizer"); + DeleteTimer(Globals->Syncer->TimerInfo); + Globals->Syncer->Exited = true; + return; + } + + Sync(); + + do + { + ULONG sigtime = GetTimerSigMask(Globals->Syncer->TimerInfo); + ULONG sigrest = GetSntpRestartSigMask(); + ULONG sigmask = SIGBREAKF_CTRL_C | sigtime | sigrest; + ULONG sigrcvd = Wait(sigmask); + + if (sigrcvd & sigtime) + { + Sync(); + } + + if (sigrcvd & sigrest) + { + ComDestroy(); + Globals->Syncer->Restart = true; + ComOpen(); + } + + if (sigrcvd & SIGBREAKF_CTRL_C) + { + Globals->Syncer->Run = false; + } + } while (Globals->Syncer->Run); + + ComDestroy(); + FreeSignal(Globals->Syncer->RestartSigBit); + DeleteTimer(Globals->Syncer->TimerInfo); + CloseSocketLibrary(); + + if (!Globals->ShuttingDown) + { + SendWarningMessage("Exited synchronizer process"); + } + + Globals->Syncer->TimerInfo = NULL; + Globals->Syncer->Task = NULL; + Globals->Syncer->Exited = true; +} + +static void ComOpen(void) +{ + if (OpenSocketLibrary() != LIB_OK) + { + // Make sure socket library is open + return; + } + + if (Globals->Syncer->FirstRun) + { + SendInfoMessage("Starting SNTP client"); + ComInit(); + Globals->Syncer->FirstRun = false; + } + + if (Globals->Syncer->Restart) + { + SendInfoMessage("Restart SNTP client"); + ComInit(); + Globals->Syncer->Restart = false; + } + + if (SyncClock() == COM_OK) + { + Globals->Syncer->FailCount = 0; + } + else + { + Globals->Syncer->FailCount++; + } + + if (Globals->Syncer->FailCount != 0 && Globals->Syncer->FailCount % 5 == 0) + { + ComDestroy(); + Globals->Syncer->Restart = true; + } + + if (Globals->Syncer->FailCount == 10) + { + SendInfoMessage("Reinitialize socket library"); + ComDestroy(); + CloseSocketLibrary(); + Globals->Syncer->FailCount = 0; + } +} + +static void ShowStats(void) +{ + if (Globals->Syncer->RunCount % 10 == 0) + { + char message[SETTINGMESSAGELEN]; + char timeString[10], dateString[10], dayString[10]; + SystemTimeString(timeString, dateString, dayString); + SNPrintf(message, SETTINGMESSAGELEN, + "Local time is %s %s %s", + dayString, dateString, timeString); + SendInfoMessage(message); + } + + if (Globals->Syncer->RunCount % 25 == 0) + { + char message[SETTINGMESSAGELEN]; + long blocks, size; + MemUsage(&blocks, &size, NULL); + SNPrintf(message, SETTINGMESSAGELEN, + "Currently using %ld bytes in %ld blocks on heap", + size, blocks); + SendInfoMessage(message); + } +} + +static void ComInit(void) +{ + if (Globals->Syncer->Client != NULL) + { + sntp_destroy(Globals->Syncer->Client); + Globals->Syncer->Client = NULL; + } + + Globals->Syncer->Client = sntp_create( + Globals->Settings->DestinationAddress, + Globals->Settings->DestinationPort, + NULL, + NULL); + + if (Globals->Syncer->Client == NULL) + { + SendWarningMessage("Could not create SNTP client"); + return; + } +} + +static int SyncClock(void) +{ + char message[SETTINGMESSAGELEN]; + struct timeval rtv, ltv, tv; + long long lt, rt, dt, adt, th; + struct ntptime nt; + bool readOnly; + int trace, ret; + + th = Globals->Settings->Threshold; + readOnly = Globals->Settings->Readonly; + trace = TraceLogging(); + + SNPrintf(message, SETTINGMESSAGELEN, + "Sending request to %s:%s", + Globals->Settings->DestinationAddress, + Globals->Settings->DestinationPort); + SendInfoMessage(message); + + ret = sntp_send(Globals->Syncer->Client); + if (ret != SNTP_OK) + { + SNPrintf(message, SETTINGMESSAGELEN, + "SNTP send failed: %s", + SntpErrorText(ret)); + SendWarningMessage(message); + return COM_ERROR; + } + + if (trace) + { + SendTraceMessage("Waiting for response"); + } + + ret = sntp_poll(Globals->Syncer->Client, Globals->Settings->Timeout); + if (ret != SNTP_OK) + { + SNPrintf(message, SETTINGMESSAGELEN, + "SNTP poll failed: %s", + SntpErrorText(ret)); + SendWarningMessage(message); + return COM_ERROR; + } + + if (trace) + { + SendTraceMessage("Processing response"); + } + + ret = sntp_recv(Globals->Syncer->Client, &nt); + if (ret != SNTP_OK) + { + SNPrintf(message, SETTINGMESSAGELEN, + "SNTP recieve failed: %s", + SntpErrorText(ret)); + SendWarningMessage(message); + + if (ret == SNTP_BACKOFF) + { + SNPrintf(message, SETTINGMESSAGELEN, + "Increasing polling interval: %ld -> %ld", + Globals->Settings->Interval, + Globals->Settings->Interval * 2); + SendWarningMessage(message); + Globals->Settings->Interval *= 2; + } + return COM_ERROR; + } + + nt2tv(&nt, &rtv); + if (trace) + { + SNPrintf(message, SETTINGMESSAGELEN, + "Got time %lu.%06lu", + (unsigned long)rtv.tv_secs, + (unsigned long)rtv.tv_micro); + SendTraceMessage(message); + } + + GetTimeOfDay(<v); + + lt = 1000000LL * ltv.tv_secs + ltv.tv_micro; + rt = 1000000LL * rtv.tv_secs + rtv.tv_micro; + dt = rt - lt; + adt = dt < 0 ? -dt : dt; + + if (trace) + { + static char out[MAXLONGLONGCHARSIZE]; + LongLongToStr(rt, out); + SNPrintf(message, SETTINGMESSAGELEN, "True time %s", out); + SendTraceMessage(message); + LongLongToStr(lt, out); + SNPrintf(message, SETTINGMESSAGELEN, "Kernel time %s", out); + SendTraceMessage(message); + LongLongToStr(dt, out); + SNPrintf(message, SETTINGMESSAGELEN, "Delta %s", out); + SendTraceMessage(message); + } + + if (!readOnly && adt < th) + { + static char out[MAXLONGLONGCHARSIZE]; + static char out2[MAXLONGLONGCHARSIZE]; + LongLongToStr(adt, out); + LongLongToStr(th, out2); + SNPrintf(message, SETTINGMESSAGELEN, + "%s us < %s us, not setting clock", + out, out2); + SendInfoMessage(message); + } + else if (!readOnly) + { + tv.tv_secs = rt / 1000000; + tv.tv_micro = rt % 1000000; + SetTimeOfDay(Globals->Syncer->TimerInfo, &tv); + + { // Log after setting clock + static char out[MAXLONGLONGCHARSIZE]; + static char out2[MAXLONGLONGCHARSIZE]; + LongLongToStr(adt, out); + LongLongToStr(th, out2); + SNPrintf(message, SETTINGMESSAGELEN, + "%s us > %s us, setting software clock", + out, out2); + SendWarningMessage(message); + SendWarningMessage("Setting hardware clock"); + SaveTimeOfDay(&tv); + } + } + + Globals->LastNtpSync = rtv; + Forbid(); + if (Globals->Window->Task != NULL && !Globals->ShuttingDown) + { + SendMessageTo(Globals->Window->UserPort, Globals->Broker->ReplyPort, ATK_REFRESH); + } + Permit(); + + return COM_OK; +} + +static const char *SntpErrorText(enum sntp_err error) +{ + switch (error) + { + case SNTP_OK: + return "OK"; + case SNTP_SYSERR: + return GetErrorText(); + case SNTP_DNSERR: + return GetHostErrorText(); + case SNTP_NOREQ: + return "No request sent"; + case SNTP_NORESP: + return "No response received"; + case SNTP_BADRESP: + return "Invalid response received"; + case SNTP_LAME: + return "Server is lame / unsynchronized"; + case SNTP_BACKOFF: + return "Polling too frequently"; + default: + return "Unknown error"; + } + return "Unknown error"; +} diff --git a/compiler.h b/compiler.h new file mode 100644 index 0000000..96a2dfe --- /dev/null +++ b/compiler.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2014-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef COMPILER_H_INCLUDED +#define COMPILER_H_INCLUDED + +#ifndef __cplusplus +typedef int bool; +#define true 1 +#define false 0 +#endif + +#ifdef __GNUC__ +#ifndef REG +#define REG(reg, arg) arg __asm(#reg) +#endif +#else +#error Need REG assembler define +#endif + +#define MAXLONGCHARSIZE 12 +#define MAXLONGLONGCHARSIZE 22 + +#endif diff --git a/config.h b/config.h new file mode 100644 index 0000000..ed41d25 --- /dev/null +++ b/config.h @@ -0,0 +1,219 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "compiler.h" + +//#define DEBUG_BUILD 1 + +#if defined(AROS) || defined(__CLIB2__) +#include +#endif + +#if defined(__libnix__) +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; +typedef uint32_t uintptr_t; +#endif + +#if defined(__CLIB2__) +#define __NO_NET_API +#define NO_INLINE_STDARG +#endif + +#if defined(AROS) || defined(__CLIB2__) +#include +#include +#endif + +#if defined(__libnix__) +#include +#include "clib/amitcp_protos.h" +#endif + +#if defined(__libnix__) || defined(__CLIB2__) +#include "netdb.h" +#include "sys/errno.h" +#include "sys/socket.h" +#include "netinet/in.h" +#else +#include +#include +#include +#include +#endif + +#ifdef MAXPATHLEN +#undef MAXPATHLEN +#endif +#ifdef MAXFILELEN +#undef MAXFILELEN +#endif +#ifdef MAXFILEPATHLEN +#undef MAXFILEPATHLEN +#endif +#define MAXPATHLEN 1792 +#define MAXFILELEN 256 +#define MAXFILEPATHLEN MAXPATHLEN + MAXFILELEN +#define MAXDOSERRORLEN 256 +#define SETTINGMESSAGELEN 128 + +#ifndef HAVE_POLL +#define POLLIN 0x0001 +struct pollfd +{ + int fd; + short events; + short revents; +}; +typedef unsigned int nfds_t; +int poll(struct pollfd *, nfds_t, int); +#endif + +#if AOS3 +#define IPTR ULONG +#endif +#if AOS3 +#define VARSIZE 0 +#else +#define VARSIZE 1 +#endif + +#ifdef AROS +#define VSNPrintf(str, n, format, args) vsnprintf(str, n, format, args) +#define SNPrintf(str, n, format, ...) snprintf(str, n, format, __VA_ARGS__) +#endif + +#define LIB_OK 0 +#define COM_OK 0 +#define LIB_ERROR -1 +#define COM_ERROR -1 + +#define APP_SHORT_NAME "AmiTimeKeeper" +#define APP_LONG_NAME "Amiga Time Keeper" +#define APP_VERSION "1.05" +#define APP_DATE_VERSION "1.05 (06.08.2018)" +#define APP_ID APP_SHORT_NAME " " APP_DATE_VERSION +#define APP_TITLE APP_LONG_NAME " " APP_DATE_VERSION +#define APP_TITLE_VERSION APP_LONG_NAME " " APP_VERSION + +#include "log.h" +#include "state.h" +#include "string.h" + +#define KEYWORD_COUNT 9 +#define KEYWORD_SERVER "SERVER" +#define KEYWORD_PORT "PORT" +#define KEYWORD_THRESHOLD "THRESHOLD" +#define KEYWORD_INTERVAL "INTERVAL" +#define KEYWORD_PRIORITY "CX_PRIORITY" +#define KEYWORD_READONLY "READONLY" +#define KEYWORD_TIMEOUT "TIMEOUT" +#define KEYWORD_VERBOSE "VERBOSE" +#define KEYWORD_LOGFILE "LOGFILE" + +#define SERVER_DEF "pool.ntp.org" +#define PORT_DEF "123" +#define THRESHOLD_MIN 1000LL +#define THRESHOLD_DEF 1000000LL +#define INTERVAL_MIN 500 +#define INTERVAL_DEF 17500 +#define PRIORITY_MIN -128 +#define PRIORITY_DEF 25 +#define PRIORITY_MAX 127 +#define READONLY_DEF 0 +#define TIMEOUT_MIN 100 +#define TIMEOUT_DEF 5000 +#define TIMEOUT_MAX 30000 +#define VERBOSE_MIN 0 +#define VERBOSE_DEF 0 +#define VERBOSE_MAX 3 +#define LOGFILE_DEF NULL + +#define KEYWORD_TEMPLATE_1 KEYWORD_READONLY "/S," KEYWORD_SERVER "/K," KEYWORD_PORT "/K," KEYWORD_TIMEOUT "/N/K," KEYWORD_THRESHOLD "/K," +#define KEYWORD_TEMPLATE_2 KEYWORD_INTERVAL "/N/K," KEYWORD_VERBOSE "/N/K," KEYWORD_PRIORITY "/N/K," KEYWORD_LOGFILE "/K" +#define KEYWORD_TEMPLATE KEYWORD_TEMPLATE_1 KEYWORD_TEMPLATE_2 + +/* broker.c */ +void StartBroker(void); +void SetBrokerPriority(int); +void ShutdownBroker(void); + +/* com.c */ +void StartCom(void); +void StopComAsync(void); +void RestartSntpAsync(void); +bool CheckComClosed(void); + +/* win_main.c */ +void ShowSettingWindow(void); +void HideSettingWindow(void); + +/* libraries.c */ +int OpenLibraries(void); +void CloseLibraries(void); + +/* net.c */ +int OpenSocketLibrary(void); +void CloseSocketLibrary(void); +const char *GetErrorText(void); +const char *GetHostErrorText(void); +int GetErrorNo(void); +int GetHostErrorNo(void); +extern struct Library *SocketBase; + +#endif diff --git a/include/clib/amitcp_protos.h b/include/clib/amitcp_protos.h new file mode 100644 index 0000000..4570685 --- /dev/null +++ b/include/clib/amitcp_protos.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015 Carsten Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SOCKET_AMITCP_H +#define _SOCKET_AMITCP_H + +#define AMITCP_BASE_NAME SocketBase +#include "inline/amitcp.h" + +#define socket Socket +#define bind Bind +#define listen Listen +#define accept Accept +#define connect Connect +#define send Send +#define sendto SendTo +#define sendmsg SendMsg +#define recv Recv +#define recvfrom RecvFrom +#define recvmsg RecvMsg +#define shutdown Shutdown +#define setsockopt SetSockOpt +#define getsockopt GetSockOpt +#define getsockname GetSockName +#define getpeername GetPeerName +#define inet_addr Inet_Addr +#define inet_network Inet_Network +#define inet_ntoa Inet_NtoA +#define gethostbyname GetHostByName +#define gethostbyaddr GetHostByAddr +#define getnetbyname GetNetByName +#define getnetbyaddr GetNetByAddr +#define getservbyname GetServByName +#define getservbyport GetServByPort +#define getprotobyname GetProtoByName +#define getprotobynumber GetProtoByNumber +#define getdtablesize GetDTableSize +#define gethostname GetHostName +#define gethostid GetHostId +#define vsyslog SyslogA +#define syslog Syslog + +#define Accept TCP_Accept +#define Bind TCP_Bind +#define CloseSocket TCP_CloseSocket +#define Connect TCP_Connect +#define Dup2Socket TCP_Dup2Socket +#define Errno TCP_Errno +#define GetDTableSize TCP_GetDTableSize +#define GetHostByAddr TCP_GetHostByAddr +#define GetHostByName TCP_GetHostByName +#define GetHostId TCP_GetHostId +#define GetHostName TCP_GetHostName +#define GetNetByAddr TCP_GetNetByAddr +#define GetNetByName TCP_GetNetByName +#define GetPeerName TCP_GetPeerName +#define GetProtoByName TCP_GetProtoByName +#define GetProtoByNumber TCP_GetProtoByNumber +#define GetServByName TCP_GetServByName +#define GetServByPort TCP_GetServByPort +#define GetSockName TCP_GetSockName +#define GetSockOpt TCP_GetSockOpt +#define GetSocketEvents TCP_GetSocketEvents +#define Inet_Addr TCP_Inet_Addr +#define Inet_LnaOf TCP_Inet_LnaOf +#define Inet_MakeAddr TCP_Inet_MakeAddr +#define Inet_NetOf TCP_Inet_NetOf +#define Inet_Network TCP_Inet_Network +#define Inet_NtoA TCP_Inet_NtoA +#define IoctlSocket TCP_IoctlSocket +#define Listen TCP_Listen +#define ObtainSocket TCP_ObtainSocket +#define Recv TCP_Recv +#define RecvFrom TCP_RecvFrom +#define RecvMsg TCP_RecvMsg +#define ReleaseCopyOfSocket TCP_ReleaseCopyOfSocket +#define ReleaseSocket TCP_ReleaseSocket +#define Send TCP_Send +#define SendMsg TCP_SendMsg +#define SendTo TCP_SendTo +#define SetErrnoPtr TCP_SetErrnoPtr +#define SetSockOpt TCP_SetSockOpt +#define SetSocketSignals TCP_SetSocketSignals +#define ShutDown TCP_ShutDown +#define Socket TCP_Socket +#define SocketBaseTagList TCP_SocketBaseTagList +#define SocketBaseTags TCP_SocketBaseTags +#define SyslogA TCP_SyslogA +#define WaitSelect TCP_WaitSelect + +#endif diff --git a/include/inline/amitcp.h b/include/inline/amitcp.h new file mode 100644 index 0000000..5ea5e14 --- /dev/null +++ b/include/inline/amitcp.h @@ -0,0 +1,203 @@ +/* Automatically generated header! Do not edit! */ + +#ifndef _INLINE_AMITCP_H +#define _INLINE_AMITCP_H + +#ifndef __INLINE_MACROS_H +#include +#endif + +#ifndef AMITCP_BASE_NAME +#define AMITCP_BASE_NAME lss->lx_BsdSocketBase +#endif + +#define TCP_Accept(s, addr, addrlen) \ + LP3(0x30, LONG, TCP_Accept, LONG, s, d0, struct sockaddr *, addr, a0, int *, addrlen, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_Bind(s, name, namelen) \ + LP3(0x24, LONG, TCP_Bind, LONG, s, d0, const struct sockaddr *, name, a0, LONG, namelen, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_CloseSocket(d) \ + LP1(0x78, LONG, TCP_CloseSocket, LONG, d, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_Connect(s, name, namelen) \ + LP3(0x36, LONG, TCP_Connect, LONG, s, d0, const struct sockaddr *, name, a0, LONG, namelen, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_Dup2Socket(fd1, fd2) \ + LP2(0x108, LONG, TCP_Dup2Socket, LONG, fd1, d0, LONG, fd2, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_Errno() \ + LP0(0xa2, LONG, TCP_Errno, \ + , AMITCP_BASE_NAME) + +#define TCP_GetDTableSize() \ + LP0(0x8a, LONG, TCP_GetDTableSize, \ + , AMITCP_BASE_NAME) + +#define TCP_GetHostByAddr(addr, len, type) \ + LP3(0xd8, struct hostent *, TCP_GetHostByAddr, const UBYTE *, addr, a0, LONG, len, d0, LONG, type, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetHostByName(name) \ + LP1(0xd2, struct hostent *, TCP_GetHostByName, const UBYTE *, name, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetHostId() \ + LP0(0x120, ULONG, TCP_GetHostId, \ + , AMITCP_BASE_NAME) + +#define TCP_GetHostName(hostname, size) \ + LP2(0x11a, LONG, TCP_GetHostName, STRPTR, hostname, a0, LONG, size, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetNetByAddr(net, type) \ + LP2(0xe4, struct netent *, TCP_GetNetByAddr, LONG, net, d0, LONG, type, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetNetByName(name) \ + LP1(0xde, struct netent *, TCP_GetNetByName, const UBYTE *, name, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetPeerName(s, hostname, namelen) \ + LP3(0x6c, LONG, TCP_GetPeerName, LONG, s, d0, struct sockaddr *, hostname, a0, int *, namelen, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetProtoByName(name) \ + LP1(0xf6, struct protoent *, TCP_GetProtoByName, const UBYTE *, name, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetProtoByNumber(proto) \ + LP1(0xfc, struct protoent *, TCP_GetProtoByNumber, LONG, proto, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetServByName(name, proto) \ + LP2(0xea, struct servent *, TCP_GetServByName, const UBYTE *, name, a0, const UBYTE *, proto, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetServByPort(port, proto) \ + LP2(0xf0, struct servent *, TCP_GetServByPort, LONG, port, d0, const UBYTE *, proto, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_GetSockName(s, hostname, namelen) \ + LP3(0x66, LONG, TCP_GetSockName, LONG, s, d0, struct sockaddr *, hostname, a0, int *, namelen, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetSockOpt(s, level, optname, optval, optlen) \ + LP5(0x60, LONG, TCP_GetSockOpt, LONG, s, d0, LONG, level, d1, LONG, optname, d2, void *, optval, a0, int *, optlen, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_GetSocketEvents(eventmaskp) \ + LP1(0x12c, LONG, TCP_GetSocketEvents, ULONG *, eventmaskp, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_Addr(cp) \ + LP1(0xb4, ULONG, TCP_Inet_Addr, const UBYTE *, cp, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_LnaOf(in) \ + LP1(0xba, ULONG, TCP_Inet_LnaOf, LONG, in, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_MakeAddr(net, host) \ + LP2(0xc6, ULONG, TCP_Inet_MakeAddr, ULONG, net, d0, ULONG, host, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_NetOf(in) \ + LP1(0xc0, ULONG, TCP_Inet_NetOf, LONG, in, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_Network(cp) \ + LP1(0xcc, ULONG, TCP_Inet_Network, const UBYTE *, cp, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_Inet_NtoA(in) \ + LP1(0xae, char *, TCP_Inet_NtoA, ULONG, in, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_IoctlSocket(d, request, argp) \ + LP3(0x72, LONG, TCP_IoctlSocket, LONG, d, d0, ULONG, request, d1, char *, argp, a0, \ + , AMITCP_BASE_NAME) + +#define TCP_Listen(s, backlog) \ + LP2(0x2a, LONG, TCP_Listen, LONG, s, d0, LONG, backlog, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_ObtainSocket(id, domain, type, protocol) \ + LP4(0x90, LONG, TCP_ObtainSocket, LONG, id, d0, LONG, domain, d1, LONG, type, d2, LONG, protocol, d3, \ + , AMITCP_BASE_NAME) + +#define TCP_Recv(s, buf, len, flags) \ + LP4(0x4e, LONG, TCP_Recv, LONG, s, d0, UBYTE *, buf, a0, LONG, len, d1, LONG, flags, d2, \ + , AMITCP_BASE_NAME) + +#define TCP_RecvFrom(s, buf, len, flags, from, fromlen) \ + LP6(0x48, LONG, TCP_RecvFrom, LONG, s, d0, UBYTE *, buf, a0, LONG, len, d1, LONG, flags, d2, struct sockaddr *, from, a1, int *, fromlen, a2, \ + , AMITCP_BASE_NAME) + +#define TCP_RecvMsg(s, msg, flags) \ + LP3(0x114, LONG, TCP_RecvMsg, LONG, s, d0, struct msghdr *, msg, a0, LONG, flags, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_ReleaseCopyOfSocket(fd, id) \ + LP2(0x9c, LONG, TCP_ReleaseCopyOfSocket, LONG, fd, d0, LONG, id, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_ReleaseSocket(fd, id) \ + LP2(0x96, LONG, TCP_ReleaseSocket, LONG, fd, d0, LONG, id, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_Send(s, msg, len, flags) \ + LP4(0x42, LONG, TCP_Send, LONG, s, d0, const UBYTE *, msg, a0, LONG, len, d1, LONG, flags, d2, \ + , AMITCP_BASE_NAME) + +#define TCP_SendMsg(s, msg, flags) \ + LP3(0x10e, LONG, TCP_SendMsg, LONG, s, d0, const struct msghdr *, msg, a0, LONG, flags, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_SendTo(s, msg, len, flags, to, tolen) \ + LP6(0x3c, LONG, TCP_SendTo, LONG, s, d0, const UBYTE *, msg, a0, LONG, len, d1, LONG, flags, d2, const struct sockaddr *, to, a1, LONG, tolen, d3, \ + , AMITCP_BASE_NAME) + +#define TCP_SetErrnoPtr(errno_p, size) \ + LP2(0xa8, LONG, TCP_SetErrnoPtr, void *, errno_p, a0, LONG, size, d0, \ + , AMITCP_BASE_NAME) + +#define TCP_SetSockOpt(s, level, optname, optval, optlen) \ + LP5(0x5a, LONG, TCP_SetSockOpt, LONG, s, d0, LONG, level, d1, LONG, optname, d2, const void *, optval, a0, LONG, optlen, d3, \ + , AMITCP_BASE_NAME) + +#define TCP_SetSocketSignals(SIGINTR, SIGIO, SIGURG) \ + LP3NR(0x84, TCP_SetSocketSignals, ULONG, SIGINTR, d0, ULONG, SIGIO, d1, ULONG, SIGURG, d2, \ + , AMITCP_BASE_NAME) + +#define TCP_ShutDown(s, how) \ + LP2(0x54, LONG, TCP_ShutDown, LONG, s, d0, LONG, how, d1, \ + , AMITCP_BASE_NAME) + +#define TCP_Socket(domain, type, protocol) \ + LP3(0x1e, LONG, TCP_Socket, LONG, domain, d0, LONG, type, d1, LONG, protocol, d2, \ + , AMITCP_BASE_NAME) + +#define TCP_SocketBaseTagList(taglist) \ + LP1(0x126, LONG, TCP_SocketBaseTagList, struct TagItem *, taglist, a0, \ + , AMITCP_BASE_NAME) + +#ifndef NO_INLINE_STDARG +#define TCP_SocketBaseTags(tags...) \ + ({ULONG _tags[] = { tags }; TCP_SocketBaseTagList((struct TagItem *)_tags); }) +#endif + +#define TCP_SyslogA(level, format, ap) \ + LP3NR(0x102, TCP_SyslogA, ULONG, level, d0, const char *, format, a0, va_list, ap, a1, \ + , AMITCP_BASE_NAME) + +#define TCP_WaitSelect(nfds, readfds, writefds, execptfds, timeout, maskp) \ + LP6(0x7e, LONG, TCP_WaitSelect, LONG, nfds, d0, fd_set *, readfds, a0, fd_set *, writefds, a1, fd_set *, execptfds, a2, struct timeval *, timeout, a3, ULONG *, maskp, d1, \ + , AMITCP_BASE_NAME) + +#endif diff --git a/include/netdb.h b/include/netdb.h new file mode 100644 index 0000000..a97e718 --- /dev/null +++ b/include/netdb.h @@ -0,0 +1,231 @@ +/* + * $Id$ + * + * :ts=8 + * + * 'Roadshow' -- Amiga TCP/IP stack + * Copyright (c) 2001-2016 by Olaf Barthel. + * All Rights Reserved. + * + * Amiga specific TCP/IP 'C' header files; + * Freely Distributable + */ + +/* + * ++Copyright++ 1980, 1983, 1988, 1993 + * - + * Copyright (c) 1980, 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * @(#)netdb.h 8.1 (Berkeley) 6/2/93 + * $NetBSD: netdb.h,v 1.8 1997/10/13 09:26:06 lukem Exp $ + */ + +#ifndef _NETDB_H +#define _NETDB_H + +/****************************************************************************/ + +#ifndef _SYS_NETINCLUDE_TYPES_H +#include "sys/netinclude_types.h" +#endif /* _SYS_NETINCLUDE_TYPES_H */ + +#ifndef _SYS_ERRNO_H +#include "sys/errno.h" +#endif /* _SYS_ERRNO_H */ + +#ifndef _NETINET_IN_H +#include "netinet/in.h" +#endif /* _NETINET_IN_H */ + +/****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack(2) + #endif +#elif defined(__VBCC__) + #pragma amiga-align +#endif + +/****************************************************************************/ + +/* This used to be in */ +#define MAXHOSTNAMELEN 256 /* max hostname size */ + +/* + * Structures returned by network data base library. All addresses are + * supplied in host order, and returned in network order (suitable for + * use in system calls). + */ +struct hostent +{ + __STRPTR h_name; /* official name of host */ + __STRPTR * h_aliases; /* alias list */ + __LONG h_addrtype; /* host address type */ + __LONG h_length; /* length of address */ + __BYTE ** h_addr_list; /* list of addresses from name server */ +#define h_addr h_addr_list[0] /* address, for backward compatiblity */ +}; + +/* + * Assumption here is that a network number + * fits in an unsigned long -- probably a poor one. + */ +struct netent +{ + __STRPTR n_name; /* official name of net */ + __STRPTR * n_aliases; /* alias list */ + __LONG n_addrtype; /* net address type */ + in_addr_t n_net; /* network # */ +}; + +struct servent +{ + __STRPTR s_name; /* official service name */ + __STRPTR * s_aliases; /* alias list */ + __LONG s_port; /* port # */ + __STRPTR s_proto; /* protocol to use */ +}; + +struct protoent +{ + __STRPTR p_name; /* official protocol name */ + __STRPTR * p_aliases; /* alias list */ + __LONG p_proto; /* protocol # */ +}; + +/* + * Error return codes from gethostbyname() and gethostbyaddr() + * (left in extern int h_errno). + */ + +#define NETDB_INTERNAL -1 /* see errno */ +#define NETDB_SUCCESS 0 /* no problem */ +#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */ +#define TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */ +#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ +#define NO_DATA 4 /* Valid name, no data record of requested type */ +#define NO_ADDRESS NO_DATA /* no address, look for MX record */ + +/****************************************************************************/ + +/* Values for getaddrinfo() and getnameinfo() */ +#define AI_PASSIVE 1 /* socket address is intended for bind() */ +#define AI_CANONNAME 2 /* request for canonical name */ +#define AI_NUMERICHOST 4 /* don't ever try hostname lookup */ +#define AI_EXT 8 /* enable non-portable extensions */ +#define AI_NUMERICSERV 16 /* don't ever try servname lookup */ +/* valid flags for addrinfo */ +#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV) + +#define NI_NUMERICHOST 1 /* return the host address, not the name */ +#define NI_NUMERICSERV 2 /* return the service address, not the name */ +#define NI_NOFQDN 4 /* return a short name if in the local domain */ +#define NI_NAMEREQD 8 /* fail if either host or service name is unknown */ +#define NI_DGRAM 16 /* look up datagram service instead of stream */ +#define NI_WITHSCOPEID 32 /* KAME hack: attach scopeid to host portion */ + +#define NI_MAXHOST MAXHOSTNAMELEN /* max host name returned by getnameinfo */ +#define NI_MAXSERV 32 /* max serv. name length returned by getnameinfo */ + +#define EAI_BADFLAGS -1 /* invalid value for ai_flags */ +#define EAI_NONAME -2 /* name or service is not known */ +#define EAI_AGAIN -3 /* temporary failure in name resolution */ +#define EAI_FAIL -4 /* non-recoverable failure in name resolution */ +#define EAI_NODATA -5 /* no address associated with name */ +#define EAI_FAMILY -6 /* ai_family not supported */ +#define EAI_SOCKTYPE -7 /* ai_socktype not supported */ +#define EAI_SERVICE -8 /* service not supported for ai_socktype */ +#define EAI_ADDRFAMILY -9 /* address family for name not supported */ +#define EAI_MEMORY -10 /* memory allocation failure */ +#define EAI_SYSTEM -11 /* system error (code indicated in errno) */ +#define EAI_BADHINTS -12 /* invalid value for hints */ +#define EAI_PROTOCOL -13 /* resolved protocol is unknown */ + +struct addrinfo { + int ai_flags; /* input flags */ + int ai_family; /* protocol family for socket */ + int ai_socktype; /* socket type */ + int ai_protocol; /* protocol for socket */ + socklen_t ai_addrlen; /* length of socket-address */ + struct sockaddr *ai_addr; /* socket-address for socket */ + char *ai_canonname; /* canonical name for service location (iff req) */ + struct addrinfo *ai_next; /* pointer to next in list */ +}; + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack() + #endif +#elif defined(__VBCC__) + #pragma default-align +#endif + +/****************************************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/****************************************************************************/ + +#endif /* !_NETDB_H */ diff --git a/include/netinet/in.h b/include/netinet/in.h new file mode 100644 index 0000000..c5dd05d --- /dev/null +++ b/include/netinet/in.h @@ -0,0 +1,275 @@ +/* + * :ts=8 + * + * 'Roadshow' -- Amiga TCP/IP stack + * Copyright (c) 2001-2016 by Olaf Barthel. + * All Rights Reserved. + * + * Amiga specific TCP/IP 'C' header files; + * Freely Distributable + */ + +/* + * Copyright (c) 1982, 1986, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in.h 8.3 (Berkeley) 1/3/94 + */ + +#ifndef _NETINET_IN_H +#define _NETINET_IN_H + +/****************************************************************************/ + +#ifndef _SYS_NETINCLUDE_TYPES_H +#include "sys/netinclude_types.h" +#endif /* _SYS_NETINCLUDE_TYPES_H */ + +#ifndef _SYS_SOCKET_H +#include "sys/socket.h" +#endif /* _SYS_SOCKET_H */ + +/****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack(2) + #endif +#elif defined(__VBCC__) + #pragma amiga-align +#endif + +/****************************************************************************/ + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * Data types. + */ +typedef unsigned long in_addr_t; +typedef unsigned short in_port_t; + +/* + * Protocols + */ +#define IPPROTO_IP 0 /* dummy for IP */ +#define IPPROTO_ICMP 1 /* control message protocol */ +#define IPPROTO_IGMP 2 /* group mgmt protocol */ +#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ +#define IPPROTO_TCP 6 /* tcp */ +#define IPPROTO_EGP 8 /* exterior gateway protocol */ +#define IPPROTO_PUP 12 /* pup */ +#define IPPROTO_UDP 17 /* user datagram protocol */ +#define IPPROTO_IDP 22 /* xns idp */ +#define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ +#define IPPROTO_EON 80 /* ISO cnlp */ +#define IPPROTO_ENCAP 98 /* encapsulation header */ + +#define IPPROTO_RAW 255 /* raw IP packet */ +#define IPPROTO_MAX 256 + + +/* + * Local port number conventions: + * Ports < IPPORT_RESERVED are reserved for + * privileged processes (e.g. root). + * Ports > IPPORT_USERRESERVED are reserved + * for servers, not necessarily privileged. + */ +#define IPPORT_RESERVED 1024 +#define IPPORT_USERRESERVED 5000 + +/* + * Internet address (a structure for historical reasons) + */ +struct in_addr { + in_addr_t s_addr; +}; + +/* + * Definitions of bits in internet address integers. + * On subnets, the decomposition of addresses to host and net parts + * is done according to subnet mask, not the masks here. + */ +#define IN_CLASSA(i) (((__ULONG)(i) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST 0x00ffffff +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(i) (((__ULONG)(i) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST 0x0000ffff +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(i) (((__ULONG)(i) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST 0x000000ff + +#define IN_CLASSD(i) (((__ULONG)(i) & 0xf0000000) == 0xe0000000) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_MULTICAST(i) IN_CLASSD(i) + +#define IN_EXPERIMENTAL(i) (((__ULONG)(i) & 0xf0000000) == 0xf0000000) +#define IN_BADCLASS(i) (((__ULONG)(i) & 0xf0000000) == 0xf0000000) + +#define INADDR_ANY 0x00000000UL +#define INADDR_BROADCAST 0xffffffffUL /* must be masked */ +#define INADDR_NONE 0xffffffff /* -1 return */ + +#define INADDR_UNSPEC_GROUP 0xe0000000UL /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001UL /* 224.0.0.1 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffUL /* 224.0.0.255 */ + +#define IN_LOOPBACKNET 127 /* official! */ + +/* + * Socket address, internet style. + */ +struct sockaddr_in { + __UBYTE sin_len; + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + __UBYTE sin_zero[8]; +}; + +/* + * Structure used to describe IP options. + * Used to store options internally, to pass them to a process, + * or to restore options retrieved earlier. + * The ip_dst is used for the first-hop gateway when using a source route + * (this gets put into the header proper). + */ +struct ip_opts { + struct in_addr ip_dst; /* first hop, 0 w/o src rt */ + __UBYTE ip_options[40]; /* actually variable in size */ +}; + +/* + * Options for use with [gs]etsockopt at the IP level. + * First word of comment is data type; bool is stored in int. + */ +#define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */ +#define IP_HDRINCL 2 /* __LONG; header is included with data */ +#define IP_TOS 3 /* __LONG; IP type of service and preced. */ +#define IP_TTL 4 /* __LONG; IP time to live */ +#define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */ +#define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */ +#define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */ +#define IP_RETOPTS 8 /* ip_opts; set/get IP options */ +#define IP_MULTICAST_IF 9 /* __UBYTE; set/get IP multicast i/f */ +#define IP_MULTICAST_TTL 10 /* __UBYTE; set/get IP multicast ttl */ +#define IP_MULTICAST_LOOP 11 /* __UBYTE; set/get IP multicast loopback */ +#define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */ +#define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */ + +/* + * Defaults and limits for options + */ +#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ +#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ +#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ + +/* + * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. + */ +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +}; + +/* + * Definitions for inet sysctl operations. + * + * Third level is protocol number. + * Fourth level is desired variable within that protocol. + */ +#define IPPROTO_MAXID (IPPROTO_IDP + 1) /* don't list to IPPROTO_MAX */ + +/* + * Names for IP sysctl objects + */ +#define IPCTL_FORWARDING 1 /* act as router */ +#define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */ +#define IPCTL_DEFTTL 3 /* default TTL */ +#ifdef notyet +#define IPCTL_DEFMTU 4 /* default MTU */ +#endif +#define IPCTL_MAXID 5 + +/****************************************************************************/ + +/* + * Macros for network/external number representation conversion. + */ +#define ntohl(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define htons(x) (x) + +#define NTOHL(x) (x) +#define NTOHS(x) (x) +#define HTONL(x) (x) +#define HTONS(x) (x) + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack() + #endif +#elif defined(__VBCC__) + #pragma default-align +#endif + +/****************************************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/****************************************************************************/ + +#endif /* _NETINET_IN_H */ diff --git a/include/sys/errno.h b/include/sys/errno.h new file mode 100644 index 0000000..f3bd297 --- /dev/null +++ b/include/sys/errno.h @@ -0,0 +1,189 @@ +/* + * :ts=8 + * + * 'Roadshow' -- Amiga TCP/IP stack + * Copyright (c) 2001-2016 by Olaf Barthel. + * All Rights Reserved. + * + * Amiga specific TCP/IP 'C' header files; + * Freely Distributable + */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_ERRNO_H +#define _SYS_ERRNO_H + +/****************************************************************************/ + +/* NOTE: the global 'extern int errno;' declaration below may conflict with + what your 'C' runtime library uses for its own 'errno' declaration. + While you should not need to #include this header file, and use + your 'C' runtime library version instead, it is sometimes not + possible to do without the error codes defined in this file. If + this is the case, define the preprocessor symbol __NO_NETINCLUDE_ERRNO + before you include this header file. */ + +/****************************************************************************/ + +#ifndef __NO_NETINCLUDE_ERRNO +extern int errno; /* global error number */ +#endif /* __NO_NETINCLUDE_ERRNO */ + +/****************************************************************************/ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* Input/output error */ +#define ENXIO 6 /* Device not configured */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EDEADLK 11 /* Resource deadlock avoided */ + /* 11 was EAGAIN */ +#define ENOMEM 12 /* Cannot allocate memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#ifndef _POSIX_SOURCE +#define ENOTBLK 15 /* Block device required */ +#endif +#define EBUSY 16 /* Device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* Operation not supported by device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Inappropriate ioctl for device */ +#ifndef _POSIX_SOURCE +#define ETXTBSY 26 /* Text file busy */ +#endif +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ + +/* math software */ +#define EDOM 33 /* Numerical argument out of domain */ +#define ERANGE 34 /* Result too large */ + +/* non-blocking and interrupt i/o */ +#define EAGAIN 35 /* Resource temporarily unavailable */ +#ifndef _POSIX_SOURCE +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define EINPROGRESS 36 /* Operation now in progress */ +#define EALREADY 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +#define ENOTSOCK 38 /* Socket operation on non-socket */ +#define EDESTADDRREQ 39 /* Destination address required */ +#define EMSGSIZE 40 /* Message too long */ +#define EPROTOTYPE 41 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 42 /* Protocol not available */ +#define EPROTONOSUPPORT 43 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#define EOPNOTSUPP 45 /* Operation not supported */ +#define EPFNOSUPPORT 46 /* Protocol family not supported */ +#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */ +#define EADDRINUSE 48 /* Address already in use */ +#define EADDRNOTAVAIL 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +#define ENETDOWN 50 /* Network is down */ +#define ENETUNREACH 51 /* Network is unreachable */ +#define ENETRESET 52 /* Network dropped connection on reset */ +#define ECONNABORTED 53 /* Software caused connection abort */ +#define ECONNRESET 54 /* Connection reset by peer */ +#define ENOBUFS 55 /* No buffer space available */ +#define EISCONN 56 /* Socket is already connected */ +#define ENOTCONN 57 /* Socket is not connected */ +#define ESHUTDOWN 58 /* Can't send after socket shutdown */ +#define ETOOMANYREFS 59 /* Too many references: can't splice */ +#define ETIMEDOUT 60 /* Operation timed out */ +#define ECONNREFUSED 61 /* Connection refused */ + +#define ELOOP 62 /* Too many levels of symbolic links */ +#endif /* _POSIX_SOURCE */ +#define ENAMETOOLONG 63 /* File name too long */ + +/* should be rearranged */ +#ifndef _POSIX_SOURCE +#define EHOSTDOWN 64 /* Host is down */ +#define EHOSTUNREACH 65 /* No route to host */ +#endif /* _POSIX_SOURCE */ +#define ENOTEMPTY 66 /* Directory not empty */ + +/* quotas & mush */ +#ifndef _POSIX_SOURCE +#define EPROCLIM 67 /* Too many processes */ +#define EUSERS 68 /* Too many users */ +#define EDQUOT 69 /* Disc quota exceeded */ + +/* Network File System */ +#define ESTALE 70 /* Stale NFS file handle */ +#define EREMOTE 71 /* Too many levels of remote in path */ +#define EBADRPC 72 /* RPC struct is bad */ +#define ERPCMISMATCH 73 /* RPC version wrong */ +#define EPROGUNAVAIL 74 /* RPC prog. not avail */ +#define EPROGMISMATCH 75 /* Program version wrong */ +#define EPROCUNAVAIL 76 /* Bad procedure for program */ +#endif /* _POSIX_SOURCE */ + +#define ENOLCK 77 /* No locks available */ +#define ENOSYS 78 /* Function not implemented */ + +#ifndef _POSIX_SOURCE +#define EFTYPE 79 /* Inappropriate file type or format */ +#define EAUTH 80 /* Authentication error */ +#define ENEEDAUTH 81 /* Need authenticator */ +#define ELAST 81 /* Must be equal largest errno */ +#endif /* _POSIX_SOURCE */ + +/****************************************************************************/ + +#endif /* _SYS_ERRNO_H */ diff --git a/include/sys/netinclude_types.h b/include/sys/netinclude_types.h new file mode 100644 index 0000000..c148481 --- /dev/null +++ b/include/sys/netinclude_types.h @@ -0,0 +1,64 @@ +/* + * :ts=8 + * + * 'Roadshow' -- Amiga TCP/IP stack + * Copyright (c) 2001-2016 by Olaf Barthel. + * All Rights Reserved. + * + * Amiga specific TCP/IP 'C' header files; + * Freely Distributable + */ + +#ifndef _SYS_NETINCLUDE_TYPES_H +#define _SYS_NETINCLUDE_TYPES_H + +/****************************************************************************/ + +/* The type definitions below mirror those in , which may + clash with local type definitions. Hence, replacements are used which + are rather unlikely to cause similar conflicts. Note that the definition + of the __TEXT and __STRPTR types currently support only SAS/C and the + GNU 'C' compiler. */ + +/****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************/ + +typedef long __LONG; /* signed 32-bit quantity */ +typedef unsigned long __ULONG; /* unsigned 32-bit quantity */ +typedef short __WORD; /* signed 16-bit quantity */ +typedef unsigned short __UWORD; /* unsigned 16-bit quantity */ +typedef signed char __BYTE; /* signed 8-bit quantity */ +typedef unsigned char __UBYTE; /* unsigned 8-bit quantity */ + +/****************************************************************************/ + +typedef void * __APTR; /* 32-bit untyped pointer */ + +/****************************************************************************/ + +#if (defined(__GNUC__) && defined(__CHAR_UNSIGNED__)) || (defined(__SASC) && defined(_UNSCHAR)) +typedef char * __STRPTR; /* string pointer (NULL terminated) */ +#else +typedef unsigned char * __STRPTR; /* string pointer (NULL terminated) */ +#endif + +#if (defined(__GNUC__) && defined(__CHAR_UNSIGNED__)) || (defined(__SASC) && defined(_UNSCHAR)) +typedef char __TEXT; /* Non-negative character */ +#else +typedef unsigned char __TEXT; /* Non-negative character */ +#endif + +/****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/****************************************************************************/ + +#endif /* !_SYS_NETINCLUDE_TYPES_H */ diff --git a/include/sys/socket.h b/include/sys/socket.h new file mode 100644 index 0000000..7d093c2 --- /dev/null +++ b/include/sys/socket.h @@ -0,0 +1,421 @@ +/* + * :ts=8 + * + * 'Roadshow' -- Amiga TCP/IP stack + * Copyright (c) 2001-2016 by Olaf Barthel. + * All Rights Reserved. + * + * Amiga specific TCP/IP 'C' header files; + * Freely Distributable + */ + +/* + * Copyright (c) 1982, 1985, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)socket.h 8.6 (Berkeley) 5/3/95 + */ + +#ifndef _SYS_SOCKET_H +#define _SYS_SOCKET_H + +/****************************************************************************/ + +/* NOTE: the 'struct timeval' structure definition differs slightly between + the AmigaOS usage (as defined in ) and the POSIX + usage (as defined in . By default, this header file + will include under the assumption that there + will be no problem as a result of that. However, if there are + compilation issues, you might want to define the 'struct timeval' + separately and disable the inclusion of , + which can be achieved by defining the preprocessor symbol + __NO_NETINCLUDE_TIMEVAL before you include this header file. */ + +/****************************************************************************/ + +#ifndef _SYS_NETINCLUDE_TYPES_H +#include +#endif /* _SYS_NETINCLUDE_TYPES_H */ + +/****************************************************************************/ + +/* + * We might reference memmove() and memset() below, which is why + * we need to make sure that both are declared somewhere. + */ +#include +#include +#include + +/****************************************************************************/ + +/* 'struct iovec', as used in a 'struct msghdr' is defined in . */ +#ifndef _SYS_UIO_H +#include +#endif /* _SYS_UIO_H */ + +/****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack(2) + #endif +#elif defined(__VBCC__) + #pragma amiga-align +#endif + +/****************************************************************************/ + +/* + * Definitions related to sockets: types, address families, options. + */ + +/* + * Data types. + */ +typedef unsigned char sa_family_t; +typedef unsigned long socklen_t; + +/* + * Types + */ +#define SOCK_STREAM 1 /* stream socket */ +#define SOCK_DGRAM 2 /* datagram socket */ +#define SOCK_RAW 3 /* raw-protocol interface */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequenced packet stream */ + +/* + * Option flags per-socket. + */ +#define SO_DEBUG 0x0001 /* turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ +#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ + +/* This is a private option which is used exclusively + * by this Amiga TCP/IP stack implementation and should not + * be used by user code. + */ +#define SO_EVENTMASK 0x2001 + +/* + * Structure used for manipulating linger option. + */ +struct linger { + __LONG l_onoff; /* option on/off */ + __LONG l_linger; /* linger time in seconds */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xffff /* options for socket level */ + +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_LOCAL 1 /* local to host (pipes, portals) */ +#define AF_UNIX AF_LOCAL /* backward compatibility */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* DEC Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* Apple Talk */ +#define AF_ROUTE 17 /* Internal Routing Protocol */ +#define AF_LINK 18 /* Link layer interface */ +#define pseudo_AF_XTP 19 /* eXpress Transfer Protocol (no AF) */ +#define AF_COIP 20 /* connection-oriented IP, aka ST II */ +#define AF_CNT 21 /* Computer Network Technology */ +#define pseudo_AF_RTIP 22 /* Help Identify RTIP packets */ +#define AF_IPX 23 /* Novell Internet Protocol */ +#define AF_SIP 24 /* Simple Internet Protocol */ +#define pseudo_AF_PIP 25 /* Help Identify PIP packets */ + +#define AF_MAX 26 + +/* + * Structure used by kernel to store most + * addresses. + */ +struct sockaddr { + __UBYTE sa_len; /* total length */ + sa_family_t sa_family; /* address family */ + __UBYTE sa_data[14]; /* actually longer; address value */ +}; + +/* + * Structure used by kernel to pass protocol + * information in raw sockets. + */ +struct sockproto { + __UWORD sp_family; /* address family */ + __UWORD sp_protocol; /* protocol */ +}; + +/* + * Protocol families, same as address families for now. + */ +#define PF_UNSPEC AF_UNSPEC +#define PF_LOCAL AF_LOCAL +#define PF_UNIX PF_LOCAL /* backward compatibility */ +#define PF_INET AF_INET +#define PF_IMPLINK AF_IMPLINK +#define PF_PUP AF_PUP +#define PF_CHAOS AF_CHAOS +#define PF_NS AF_NS +#define PF_ISO AF_ISO +#define PF_OSI AF_ISO +#define PF_ECMA AF_ECMA +#define PF_DATAKIT AF_DATAKIT +#define PF_CCITT AF_CCITT +#define PF_SNA AF_SNA +#define PF_DECnet AF_DECnet +#define PF_DLI AF_DLI +#define PF_LAT AF_LAT +#define PF_HYLINK AF_HYLINK +#define PF_APPLETALK AF_APPLETALK +#define PF_ROUTE AF_ROUTE +#define PF_LINK AF_LINK +#define PF_XTP pseudo_AF_XTP /* really just proto family, no AF */ +#define PF_COIP AF_COIP +#define PF_CNT AF_CNT +#define PF_SIP AF_SIP +#define PF_IPX AF_IPX /* same format as AF_NS */ +#define PF_RTIP pseudo_AF_FTIP /* same format as AF_INET */ +#define PF_PIP pseudo_AF_PIP + +#define PF_MAX AF_MAX + +/* + * Definitions for network related sysctl, CTL_NET. + * + * Second level is protocol family. + * Third level is protocol number. + * + * Further levels are defined by the individual families below. + */ +#define NET_MAXID AF_MAX + +/* + * PF_ROUTE - Routing table + * + * Three additional levels are defined: + * Fourth: address family, 0 is wildcard + * Fifth: type of info, defined below + * Sixth: flag(s) to mask with for NET_RT_FLAGS + */ +#define NET_RT_DUMP 1 /* dump; may limit to a.f. */ +#define NET_RT_FLAGS 2 /* by flags, e.g. RESOLVING */ +#define NET_RT_IFLIST 3 /* survey interface list */ +#define NET_RT_MAXID 4 + +/* + * Maximum queue length specifiable by listen. + */ +#define SOMAXCONN 5 + +/* + * Message header for recvmsg and sendmsg calls. + * Used value-result for recvmsg, value only for sendmsg. + */ +struct msghdr { + __APTR msg_name; /* optional address */ + socklen_t msg_namelen; /* size of address */ + struct iovec * msg_iov; /* scatter/gather array */ + __ULONG msg_iovlen; /* # elements in msg_iov */ + __APTR msg_control; /* ancillary data, see below */ + socklen_t msg_controllen; /* ancillary data buffer len */ + __LONG msg_flags; /* flags on received message */ +}; + +#define MSG_OOB 0x1 /* process out-of-band data */ +#define MSG_PEEK 0x2 /* peek at incoming message */ +#define MSG_DONTROUTE 0x4 /* send without using routing tables */ +#define MSG_EOR 0x8 /* data completes record */ +#define MSG_TRUNC 0x10 /* data discarded before delivery */ +#define MSG_CTRUNC 0x20 /* control data lost before delivery */ +#define MSG_WAITALL 0x40 /* wait for full request or error */ +#define MSG_DONTWAIT 0x80 /* this message should be nonblocking */ + +/* + * Header for ancillary data objects in msg_control buffer. + * Used for additional information with/about a datagram + * not expressible by flags. The format is a sequence + * of message elements headed by cmsghdr structures. + */ +struct cmsghdr { + socklen_t cmsg_len; /* data byte count, including hdr */ + __LONG cmsg_level; /* originating protocol */ + __LONG cmsg_type; /* protocol-specific type */ +/* followed by __UBYTE cmsg_data[]; */ +}; + +/* given pointer to struct cmsghdr, return pointer to data */ +#define CMSG_DATA(cmsg) ((__UBYTE *)((cmsg) + 1)) + +/* given pointer to struct cmsghdr, return pointer to next cmsghdr */ +#define CMSG_NXTHDR(mhdr, cmsg) \ + (((__APTR)(cmsg) + (cmsg)->cmsg_len + sizeof(struct cmsghdr) > \ + (mhdr)->msg_control + (mhdr)->msg_controllen) ? \ + (struct cmsghdr *)NULL : \ + (struct cmsghdr *)((__APTR)(cmsg) + ALIGN((cmsg)->cmsg_len))) + +#define CMSG_FIRSTHDR(mhdr) ((struct cmsghdr *)(mhdr)->msg_control) + +/* "Socket"-level control message types: */ +#define SCM_RIGHTS 0x01 /* access rights (array of __LONG) */ + +/* + * The following comes from the original header file, + * which has been retired in favour of the + * type definitions. What remains are the macros in support of the + * "select()" call and those for endian-neutral operations. + */ + +/****************************************************************************/ + +/* + * In case the select() data structures and macros are already + * defined by somebody else... + */ + +#ifndef FD_SET + +#define NBBY 8 /* number of bits in a byte */ + +/* + * Select uses bit masks of file descriptors in longs. These macros + * manipulate such bit fields (the filesystem macros use chars). + * FD_SETSIZE may be defined by the user, but the default here should + * be enough for most uses. + */ +#ifndef FD_SETSIZE +#define FD_SETSIZE 256 +#endif + +typedef unsigned long fd_mask; +#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ + +#ifndef howmany +#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#endif + +typedef struct fd_set { + fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} fd_set; + +#define FD_SET(n, p) ((void)(((unsigned long)n) < FD_SETSIZE ? (p)->fds_bits[((unsigned long)n)/NFDBITS] |= (1 << (((unsigned long)n) % NFDBITS)) : 0)) +#define FD_CLR(n, p) ((void)(((unsigned long)n) < FD_SETSIZE ? (p)->fds_bits[((unsigned long)n)/NFDBITS] &= ~(1 << (((unsigned long)n) % NFDBITS)) : 0)) +#define FD_ISSET(n, p) (((unsigned long)n) < FD_SETSIZE && ((p)->fds_bits[((unsigned long)n)/NFDBITS] & (1 << (((unsigned long)n) % NFDBITS)))) +#define FD_COPY(f, t) ((void)memmove((t), (f), sizeof(*(f)))) +#define FD_ZERO(p) ((void)memset((p), 0, sizeof(*(p)))) + +#endif /* FD_SET */ + +/****************************************************************************/ + +#ifdef __GNUC__ + #ifdef __PPC__ + #pragma pack() + #endif +#elif defined(__VBCC__) + #pragma default-align +#endif + +/****************************************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/****************************************************************************/ + +#ifndef __NO_NETINCLUDE_TIMEVAL + +/* + * This is for compatibility with POSIX-like 'timeval' structures + * which are remarkably similar to the Amiga 'timeval' except for + * the structure member names... + */ +#ifndef DEVICES_TIMER_H +#include +#endif /* DEVICES_TIMER_H */ + +#ifndef tv_sec +#define tv_sec tv_secs +#endif /* tv_sec */ + +#ifndef tv_usec +#define tv_usec tv_micro +#endif /* tv_usec */ + +#endif /* __NO_NETINCLUDE_TIMEVAL */ + +/****************************************************************************/ + +#endif /* !_SYS_SOCKET_H_ */ diff --git a/library.c b/library.c new file mode 100644 index 0000000..39264f8 --- /dev/null +++ b/library.c @@ -0,0 +1,341 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "time.h" +#include "mem.h" + +/* Library identifiers */ +static const char *dosName = "dos.library"; +static const char *aslName = "asl.library"; +static const char *iconName = "icon.library"; +static const char *commoditiesName = "commodities.library"; +static const char *localeName = "locale.library"; +static const char *utilityName = "utility.library"; +static const char *gadgetName = "gadtools.library"; +static const char *intuitionName = "intuition.library"; +static const char *mathName = "mathieeedoubbas.library"; + +#if !defined(LIB_HOST) +/* Use fake bases */ +#define DOSBase __FX_DOSBase +#define LocaleBase __FX_LocaleBase +#define UtilityBase __FX_UtilityBase +#define IntuitionBase __FX_IntuitionBase +#define CxBase __FX_CxBase +#define AslBase __FX_AslBase +#define IconBase __FX_IconBase +#define GadToolsBase __FX_GadToolsBase +#define __MathIeeeDoubBasBase __FX__MathIeeeDoubBasBase +#define __UtilityBase __FX__UtilityBase +#endif + +/* Library addresses */ +//struct ExecBase *SysBase = (struct ExecBase *)4L; +struct DosLibrary *DOSBase = NULL; +struct LocaleBase *LocaleBase = NULL; +struct UtilityBase *UtilityBase = NULL; +struct IntuitionBase *IntuitionBase = NULL; +struct Library *CxBase = NULL; +struct Library *AslBase = NULL; +struct Library *IconBase = NULL; +struct Library *GadToolsBase = NULL; +struct Library *__MathIeeeDoubBasBase = NULL; +struct UtilityBase *__UtilityBase = NULL; + +#define DOSLIB_NAME ((STRPTR)dosName) +#define DOSLIB_REV 36L +#define ASLLIB_NAME ((STRPTR)aslName) +#define ASLLIB_REV 37L +#define ICONLIB_NAME ((STRPTR)iconName) +#define ICONLIB_REV 36L +#define COMMODLIB_NAME ((STRPTR)commoditiesName) +#define COMMODLIB_REV 37L +#define INTUITIONLIB_NAME ((STRPTR)intuitionName) +#define INTUITIONLIB_REV 37L +#define LOCALELIB_NAME ((STRPTR)localeName) +#define LOCALELIB_REV 37L +#define UTILLIB_NAME ((STRPTR)utilityName) +#define UTILLIB_REV 37L +#define GADGETLIB_NAME ((STRPTR)gadgetName) +#define GADGETLIB_REV 37L +#define MATHLIB_NAME ((STRPTR)mathName) +#define MATHLIB_REV 34L + +// RTC Clock +static const char *batteryClockName = "battclock.resource"; +#define BATTCLOCK_NAME ((STRPTR)batteryClockName) +struct Library *BattClockBase = NULL; + +static struct Device *TimerDevice = NULL; +static const char *currentLocale = "Current Locale"; + +static void OpenLibrarySuccess(STRPTR name) +{ + LogTrace("Opened %s", name); +} + +static void OpenLibraryError(STRPTR name, long version) +{ + LogError("Cannot open %s %ld.0", name, version); +} + +static void ClosingLibrary(STRPTR name) +{ + LogTrace("Closing %s", name); +} + +static void OpenResourceSuccess(const char *name) +{ + LogTrace("Opened %s", name); +} + +static void OpenResourceError(const char *name) +{ + LogError("Cannot open %s", name); +} + +static void ClosingResource(const char *name) +{ + LogTrace("Closing %s", name); +} + +int OpenLibraries(void) +{ + // DOS Library + if (!(DOSBase = (struct DosLibrary *)OpenLibrary((STRPTR)DOSLIB_NAME, DOSLIB_REV))) + { + OpenLibraryError(DOSLIB_NAME, DOSLIB_REV); + return LIB_ERROR; + } + +#ifdef AOS3 + // Library does not always provide IdString + OpenResourceSuccess(DOSLIB_NAME); +#else + OpenLibrarySuccess(((struct Library *)DOSBase)->lib_IdString); +#endif + + // Math Library + if (!(__MathIeeeDoubBasBase = OpenLibrary((STRPTR)MATHLIB_NAME, MATHLIB_REV))) + { + OpenLibraryError(MATHLIB_NAME, MATHLIB_REV); + return LIB_ERROR; + } + +#ifdef AOS3 + // Library does not always provide IdString + OpenResourceSuccess(MATHLIB_NAME); +#else + OpenLibrarySuccess(((struct Library *)__MathIeeeDoubBasBase)->lib_IdString); +#endif + + // Commodities Library + if (!(CxBase = OpenLibrary((STRPTR)COMMODLIB_NAME, COMMODLIB_REV))) + { + OpenLibraryError(COMMODLIB_NAME, COMMODLIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(CxBase->lib_IdString); + + // Intuition Library + if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary((STRPTR)INTUITIONLIB_NAME, INTUITIONLIB_REV))) + { + OpenLibraryError(INTUITIONLIB_NAME, INTUITIONLIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(((struct Library *)IntuitionBase)->lib_IdString); + + // Icon Library + if (!(IconBase = OpenLibrary((STRPTR)ICONLIB_NAME, ICONLIB_REV))) + { + OpenLibraryError(ICONLIB_NAME, ICONLIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(IconBase->lib_IdString); + + // Icon Library + if (!(AslBase = OpenLibrary((STRPTR)ASLLIB_NAME, ASLLIB_REV))) + { + OpenLibraryError(ASLLIB_NAME, ASLLIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(AslBase->lib_IdString); + + // Locale Library + if (!(LocaleBase = (struct LocaleBase *)OpenLibrary((STRPTR)LOCALELIB_NAME, LOCALELIB_REV))) + { + OpenLibraryError(LOCALELIB_NAME, LOCALELIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(((struct Library *)LocaleBase)->lib_IdString); + + // Utility Library + if (!(UtilityBase = (struct UtilityBase *)OpenLibrary((STRPTR)UTILLIB_NAME, UTILLIB_REV))) + { + OpenLibraryError(UTILLIB_NAME, UTILLIB_REV); + return LIB_ERROR; + } + + __UtilityBase = UtilityBase; + OpenLibrarySuccess(((struct Library *)UtilityBase)->lib_IdString); + + // Gadget Library + if (!(GadToolsBase = OpenLibrary((STRPTR)GADGETLIB_NAME, GADGETLIB_REV))) + { + OpenLibraryError(GADGETLIB_NAME, GADGETLIB_REV); + return LIB_ERROR; + } + + OpenLibrarySuccess(GadToolsBase->lib_IdString); + + // Locale + if (!(Globals->Locale = OpenLocale(NULL))) + { + OpenResourceError(currentLocale); + return LIB_ERROR; + } + + OpenResourceSuccess((const char *)Globals->Locale->loc_LocaleName); + + // RTC Clock + if (!(BattClockBase = OpenResource((STRPTR)BATTCLOCK_NAME))) + { + OpenResourceError((const char *)BATTCLOCK_NAME); + return LIB_ERROR; + } + + OpenResourceSuccess((const char *)BATTCLOCK_NAME); + + // Timer Device + if (!(TimerDevice = OpenTimerBase())) + { + OpenResourceError(TIMERNAME); + return LIB_ERROR; + } + + OpenResourceSuccess(TIMERNAME); + + InitUtcOffset(); + + return LIB_OK; +} + +void CloseLibraries(void) +{ + if (TimerDevice != NULL) + { + ClosingResource((const char *)TIMERNAME); + CloseTimerBase(); + TimerDevice = NULL; + } + + if (Globals->Locale != NULL) + { + ClosingResource((const char *)Globals->Locale->loc_LocaleName); + CloseLocale(Globals->Locale); + Globals->Locale = NULL; + } + + if (CxBase != NULL) + { + ClosingLibrary(CxBase->lib_IdString); + CloseLibrary(CxBase); + CxBase = NULL; + } + + if (AslBase != NULL) + { + ClosingLibrary(AslBase->lib_IdString); + CloseLibrary(AslBase); + AslBase = NULL; + } + + if (IconBase != NULL) + { + ClosingLibrary(IconBase->lib_IdString); + CloseLibrary(IconBase); + IconBase = NULL; + } + + if (LocaleBase != NULL) + { + ClosingLibrary(((struct Library *)LocaleBase)->lib_IdString); + CloseLibrary((struct Library *)LocaleBase); + LocaleBase = NULL; + } + + if (UtilityBase != NULL) + { + ClosingLibrary(((struct Library *)UtilityBase)->lib_IdString); + CloseLibrary((struct Library *)UtilityBase); + UtilityBase = NULL; + __UtilityBase = NULL; + } + + if (GadToolsBase != NULL) + { + ClosingLibrary(GadToolsBase->lib_IdString); + CloseLibrary(GadToolsBase); + GadToolsBase = NULL; + } + + if (IntuitionBase != NULL) + { + ClosingLibrary(((struct Library *)IntuitionBase)->lib_IdString); + CloseLibrary((struct Library *)IntuitionBase); + IntuitionBase = NULL; + } + + if (DOSBase != NULL) + { +#ifdef AOS3 + // Library does not always provide IdString + ClosingResource(DOSLIB_NAME); +#else + ClosingLibrary(((struct Library *)DOSBase)->lib_IdString); +#endif + CloseLibrary((struct Library *)DOSBase); + DOSBase = NULL; + } + + if (__MathIeeeDoubBasBase != NULL) + { +#ifdef AOS3 + // Library does not always provide IdString + ClosingResource(MATHLIB_NAME); +#else + ClosingLibrary(((struct Library *)__MathIeeeDoubBasBase)->lib_IdString); +#endif + CloseLibrary((struct Library *)__MathIeeeDoubBasBase); + __MathIeeeDoubBasBase = NULL; + } +} diff --git a/log.c b/log.c new file mode 100644 index 0000000..51b63f5 --- /dev/null +++ b/log.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "log.h" +#include "mem.h" +#include + +#define MAXLOGLINESIZE 255 + +enum LogSeverity +{ + ErrorMessage = 5, + WarningMessage = 10, + InfoMessage = 20, + TraceMessage = 30 +}; + +struct LogEntry +{ + enum LogSeverity level; + char *line; + struct LogEntry *next; +}; + +static bool logDisabled = false; +static bool bufferLog = false; +static BPTR logFile = NULL; +static struct LogEntry *firstEntry = NULL; +static struct LogEntry *lastEntry = NULL; + +void OpenLogFile(void) +{ + BPTR lock; + bool deleteFirst = false; + + if (Globals->Settings->LogFile == NULL) + { + return; + } + + lock = Lock((STRPTR)Globals->Settings->LogFile, ACCESS_READ); + if (lock) + { + deleteFirst = true; + UnLock(lock); + } + + if (deleteFirst) + { + bool success; + LogWarning("Deleting existing log file %s", Globals->Settings->LogFile); + success = DeleteFile((STRPTR)Globals->Settings->LogFile); + if (!success) + { + char message[MAXDOSERRORLEN]; + long error = IoErr(); + Fault(error, (STRPTR) "Cannot delete existing log file %s", (STRPTR)message, MAXDOSERRORLEN); + LogWarning(message); + logFile = NULL; + return; + } + } + + LogInfo("Opening log file %s", Globals->Settings->LogFile); + logFile = Open((STRPTR)Globals->Settings->LogFile, MODE_READWRITE); + if (!logFile) + { + char message[MAXDOSERRORLEN]; + long error = IoErr(); + Fault(error, (STRPTR) "Cannot open log file %s", (STRPTR)message, MAXDOSERRORLEN); + LogWarning(message); + logFile = NULL; + return; + } +} + +void CloseLogFile(void) +{ + if (logFile == NULL) + { + return; + } + + LogTrace("Closing log file %s", Globals->Settings->LogFile); + Close(logFile); + logFile = NULL; + logDisabled = true; +} + +bool TraceLogging(void) +{ + return Globals->Settings->Verbose * 10 >= (int)TraceMessage + ? true + : false; +} + +static void VLogLine(enum LogSeverity level, const char *format, va_list ap) +{ + struct LogEntry *entry; + char line[MAXLOGLINESIZE + 1]; + + if (logDisabled) + { + return; + } + + if (bufferLog || Globals->Settings->Verbose * 10 >= (int)level) + { + int len; + VSNPrintf(line, MAXLOGLINESIZE - 1, format, ap); + len = TrimRight(line); + if (len != 0) + { + line[len++] = '\n'; + line[len] = '\0'; + } + } + + if (bufferLog) + { + entry = (struct LogEntry *)AllocMemSafe(sizeof(struct LogEntry)); + entry->level = level; + entry->line = StrDupSafe(line); + entry->next = NULL; + + if (firstEntry == NULL) + { + firstEntry = entry; + lastEntry = entry; + } + else + { + lastEntry->next = entry; + lastEntry = entry; + } + return; + } + + if (Globals->Settings->Verbose * 10 >= (int)level) + { + if (logFile != NULL) + { + FPuts(logFile, (STRPTR)line); + Flush(logFile); + } + else + { + Printf((STRPTR)line); + } + } +} + +void LogTrace(const char *format, ...) +{ + va_list args; + va_start(args, format); + VLogLine(TraceMessage, format, args); + va_end(args); +} + +void LogInfo(const char *format, ...) +{ + va_list args; + va_start(args, format); + VLogLine(InfoMessage, format, args); + va_end(args); +} + +void LogWarning(const char *format, ...) +{ + va_list args; + va_start(args, format); + VLogLine(WarningMessage, format, args); + va_end(args); +} + +void LogError(const char *format, ...) +{ + va_list args; + va_start(args, format); + VLogLine(ErrorMessage, format, args); + va_end(args); +} + +void SetLogBuffer(void) +{ + bufferLog = true; +} + +void FlushLogBuffer(void) +{ + struct LogEntry *current; + + if (!bufferLog) + return; + + current = firstEntry; + while (current != NULL) + { + struct LogEntry *last; + if (Globals->Settings->Verbose * 10 >= (int)current->level) + { + if (logFile != NULL) + { + FPuts(logFile, (STRPTR)current->line); + } + else + { + Printf((STRPTR)current->line); + } + } + + last = current; + current = current->next; + + FreeMemSafe(last->line); + FreeMemSafe(last); + } + + bufferLog = false; + firstEntry = NULL; + lastEntry = NULL; +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..a404c5e --- /dev/null +++ b/log.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef LOG_H_INCLUDED +#define LOG_H_INCLUDED + +#include "compiler.h" + +void LogTrace(const char *format, ...); +void LogInfo(const char *format, ...); +void LogWarning(const char *format, ...); +void LogError(const char *format, ...); +bool TraceLogging(void); +void OpenLogFile(void); +void CloseLogFile(void); +void SetLogBuffer(void); +void FlushLogBuffer(void); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..4a0bcdf --- /dev/null +++ b/main.c @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "state.h" +#include "time.h" +#include "mem.h" + +#include + +const char *vers = "\0$VER: " APP_ID; +static const char *template = KEYWORD_TEMPLATE; + +static void LogStartMessage(void); +static void LogLocalTime(void); +static void GetCliSettings(void); +static void GetWbSettings(struct WBStartup *); + +int main(int argc, char **argv) +{ + InitState(); + SetLogBuffer(); + LogStartMessage(); + LogLocalTime(); + + if (OpenLibraries() != 0) + return RETURN_ERROR; + + LoadSettings(); + + if (argc != 0) + GetCliSettings(); + else + GetWbSettings((struct WBStartup *)argv); + + ApplySettings(); + SanitizeSettings(); + ShowSettings(); + OpenLogFile(); + FlushLogBuffer(); + + StartBroker(); + + LogLocalTime(); + CloseSocketLibrary(); + CloseLibraries(); + CloseLogFile(); + DestroyState(); + FreeAllSafe(); + + return RETURN_OK; +} + +static void GetCliSettings(void) +{ + struct AppSettings *settings; + struct RDArgs *inArgs; + long args[KEYWORD_COUNT]; + int i; + + for (i = 0; i < KEYWORD_COUNT; i++) + args[i] = 0; + + settings = CreateSettings(CliSettingType); + settings->Verbose = 1; + + inArgs = ReadArgs((void *)template, (void *)&args, NULL); + if (inArgs) + { + // Keyword order in template needs to match order in settingFunctions + for (i = 0; i < KEYWORD_COUNT; i++) + { + if (args[i] != 0) + { + settingFunctions[i].Function(settings, (void *)args[i]); + } + } + FreeArgs(inArgs); + } + + CacheSettings(settings); +} + +static void GetWbSettings(struct WBStartup *wbs) +{ + struct AppSettings *settings; + struct DiskObject *diskObject; + STRPTR filename, arg; + BPTR oldDir; + int argNo, i; + + filename = (STRPTR)AllocMemSafe(MAXFILEPATHLEN); + settings = CreateSettings(WbSettingType); + + for (argNo = 0; argNo < wbs->sm_NumArgs; ++argNo) + { + if (wbs->sm_ArgList[argNo].wa_Lock != NULL) + { + oldDir = CurrentDir(wbs->sm_ArgList[argNo].wa_Lock); + diskObject = GetDiskObjectNew((void *)wbs->sm_ArgList[argNo].wa_Name); + if (diskObject) + { + for (i = 0; i < KEYWORD_COUNT; i++) + { + arg = FindToolType(diskObject->do_ToolTypes, (STRPTR)settingFunctions[i].Name); + if (arg) + { + settingFunctions[i].Function(settings, (char *)arg); + } + } + FreeDiskObject(diskObject); + } + CurrentDir(oldDir); + } + } + + CacheSettings(settings); + FreeMemSafe(filename); +} + +static void LogStartMessage(void) +{ + LogWarning("%s", APP_TITLE); +} + +static void LogLocalTime(void) +{ + char timeString[10], dateString[10], dayString[10]; + SystemTimeString(timeString, dateString, dayString); + LogWarning("Local time is %s %s %s", dayString, dateString, timeString); +} diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..33c77c2 --- /dev/null +++ b/mem.c @@ -0,0 +1,297 @@ +/*- + * Copyright (c) 2014-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#define ALLOC_MEM(x) AllocVec(x, MEMF_ANY | MEMF_CLEAR) +#define FREE_MEM(x) FreeVec(x) + +#include "compiler.h" +#include "log.h" +int StrLen(const char *); + +#if defined(__x86_64__) || defined(__aarch64__) || \ + defined(_M_AMD64) || defined(_M_ARM64) || \ + defined(__powerpc64__) +#define P64BIT +#endif + +#ifdef DEBUG_BUILD +#define MEM_TRACE 1 +#endif + +/** + * @brief Block of allocated memory. + */ +struct MemoryBlock +{ + struct MemoryBlock *next; + size_t size; + void *address; +}; + +/** + * @brief List of allocated memory. Uses the LIFO principle. + */ +struct MemoryList +{ + struct MemoryBlock *first; + size_t peak; + size_t size; + long count; +}; + +/** + * @brief Global list of allocated memory. + */ +static struct MemoryList *list = NULL; + +void AllocationError(char *, size_t); +void DeAllocationError(char *, void *); + +/** + * @brief Allocate memory and add it to the global memory list. + */ +void *AllocMemSafe(size_t size) +{ + struct MemoryBlock *newblock; + size_t allocsize; + + Forbid(); + if (list == NULL) + { + list = (struct MemoryList *)ALLOC_MEM(sizeof(struct MemoryList)); + if (!list) + { + AllocationError("list", sizeof(struct MemoryList)); + return 0; + } + + list->first = NULL; + list->peak = 0; + list->size = 0; + list->count = 0; + } + +#ifdef P64BIT + // Align to bytes of 8 + allocsize = (size + 7) & ~0x07; +#else + // Align to bytes of 4 + allocsize = (size + 3) & ~0x03; +#endif + + newblock = (struct MemoryBlock *)ALLOC_MEM(sizeof(struct MemoryBlock)); + if (!newblock) + { + AllocationError("block", sizeof(struct MemoryBlock)); + return 0; + } + + newblock->address = (struct MemoryBlock *)ALLOC_MEM(allocsize); + if (!newblock->address) + { + FREE_MEM(newblock); + AllocationError("memory", allocsize); + return 0; + } + + newblock->size = allocsize; + newblock->next = list->first; + list->first = newblock; + list->size += allocsize; + list->count++; + + if (list->size > list->peak) + { + list->peak = list->size; + } + Permit(); + +#ifdef MEM_TRACE + Printf("Allocated memory block off %ld bytes at 0x%lx\n", + newblock->size, newblock->address); +#endif + + // Memory allocated + return newblock->address; +} + +void RemoveMemSafe(void *block, bool deallocate) +{ + struct MemoryBlock *current, *previous; + + Forbid(); + if (list == NULL || block == NULL) + { + DeAllocationError("list", 0); + return; + } + + if (block == NULL) + { + DeAllocationError("memory", 0); + return; + } + + previous = NULL; + current = list->first; + while (current != NULL && current->address != block) + { + previous = current; + current = current->next; + } + + if (current == NULL) + { + DeAllocationError("address not found", block); + return; + } + + if (previous == NULL) + { + list->first = current->next; + } + else + { + previous->next = current->next; + } + + list->size -= current->size; + list->count--; + + if (deallocate) + { + FREE_MEM(current->address); + } + + current->address = NULL; + current->next = NULL; + current->size = 0; + FREE_MEM(current); + Permit(); +} + +/** + * @brief Deallocate memory from the global memory list. + */ +void FreeMemSafe(void *block) +{ + RemoveMemSafe(block, true); +} + +void *MemDupSafe(const void *s1, size_t len) +{ + char *dup; + dup = AllocMemSafe(len); + CopyMem((void *)s1, dup, len); + return dup; +} + +char *StrDupSafe(const char *s1) +{ + char *s2; + size_t len = s1 != NULL ? StrLen(s1) : 1; + s2 = AllocMemSafe(++len); + + if (s2 == NULL) + { + return NULL; + } + + CopyMem((void *)s1, s2, --len); + return s2; +} + +/** + * @brief Deallocate all memory in the global memory list. + */ +void FreeAllSafe(void) +{ + struct MemoryBlock *current, *next; + + Forbid(); + if (list == NULL) + { + return; + } + + current = list->first; + while (current != NULL) + { + LogTrace("Released forgotten memory block on %ld bytes at 0x%lx", + (long)current->size, + (unsigned long)current->address); + next = current->next; + FREE_MEM(current->address); + FREE_MEM(current); + current = next; + } + + FREE_MEM(list); + list = NULL; + Permit(); +} + +/** + * @brief Get memory usage in the global memory list. + */ +void MemUsage(long *blocks, long *size, long *peak) +{ + if (blocks != NULL) + { + *blocks = list->count; + } + + if (size != NULL) + { + *size = (long)list->size; + } + + if (peak != NULL) + { + *peak = (long)list->peak; + } +} + +/** + * @brief Log a memory allocation error. + */ +void AllocationError(char *descr, size_t size) +{ + LogTrace("Memory allocation error (%s) with size (%ld)", descr, (long)size); +} + +/** + * @brief Log a memory deallocation error. + */ +void DeAllocationError(char *descr, void *p) +{ + LogTrace("Memory deallocation error (%s) at 0x%lx", descr, p); +} diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..3770d79 --- /dev/null +++ b/mem.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2014-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef MEM_H_INCLUDED +#define MEM_H_INCLUDED + +#include + +void *AllocMemSafe(size_t); +char *StrDupSafe(const char *); +void *MemDupSafe(const void *s1, size_t len); +void FreeMemSafe(void *); +void FreeAllSafe(void); +void MemUsage(long *, long *, long *); + +#endif diff --git a/message.c b/message.c new file mode 100644 index 0000000..9241437 --- /dev/null +++ b/message.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "message.h" +#include "mem.h" + +static void SendTextMessage(char *text, long type) +{ + Forbid(); + if (!Globals->ShuttingDown) + { + struct AppWindowMessage *message = (struct AppWindowMessage *)AllocMemSafe(sizeof(struct AppWindowMessage)); + message->Msg.mn_Node.ln_Type = NT_MESSAGE; + message->Msg.mn_Length = sizeof(struct AppWindowMessage); + message->Msg.mn_ReplyPort = Globals->Broker->ReplyPort; + message->Type = type; + message->Text = StrDupSafe(text); + PutMsg(Globals->Broker->UserPort, (struct Message *)message); + } + Permit(); +} + +void SendErrorMessage(char *text) +{ + SendTextMessage(text, ATK_LOGERROR); +} + +void SendWarningMessage(char *text) +{ + SendTextMessage(text, ATK_LOGWARN); +} + +void SendInfoMessage(char *text) +{ + SendTextMessage(text, ATK_LOGINFO); +} + +void SendTraceMessage(char *text) +{ + SendTextMessage(text, ATK_LOGTRACE); +} + +void SendMessage(long type) +{ + Forbid(); + if (!Globals->ShuttingDown) + { + struct AppWindowMessage *message = (struct AppWindowMessage *)AllocMemSafe(sizeof(struct AppWindowMessage)); + message->Msg.mn_Node.ln_Type = NT_MESSAGE; + message->Msg.mn_Length = sizeof(struct AppWindowMessage); + message->Msg.mn_ReplyPort = Globals->Broker->ReplyPort; + message->Type = type; + message->Text = NULL; + PutMsg(Globals->Broker->UserPort, (struct Message *)message); + } + Permit(); +} + +void SendMessageTo(struct MsgPort *dest, struct MsgPort *reply, long type) +{ + Forbid(); + if (!Globals->ShuttingDown) + { + struct AppWindowMessage *message = (struct AppWindowMessage *)AllocMemSafe(sizeof(struct AppWindowMessage)); + message->Msg.mn_Node.ln_Type = NT_MESSAGE; + message->Msg.mn_Length = sizeof(struct AppWindowMessage); + message->Msg.mn_ReplyPort = reply; + message->Type = type; + message->Text = NULL; + PutMsg(dest, (struct Message *)message); + } + Permit(); +} diff --git a/message.h b/message.h new file mode 100644 index 0000000..3fa976e --- /dev/null +++ b/message.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef MESSAGE_H_INCLUDED +#define MESSAGE_H_INCLUDED + +#include "config.h" + +#define ATK_LOGERROR 1 +#define ATK_LOGWARN 2 +#define ATK_LOGINFO 3 +#define ATK_LOGTRACE 4 +#define ATK_RESTART 10 +#define ATK_REFRESH 11 +#define ATK_STORE 13 +#define ATK_APPLY 14 +#define ATK_UNDO 15 +#define ATK_ENABLE 20 +#define ATK_DISABLE 21 +#define ATK_READONLY 22 +#define ATK_READWRITE 23 + +struct AppWindowMessage +{ + struct Message Msg; + long Type; + char *Text; +}; + +void SendMessage(long); +void SendErrorMessage(char *); +void SendWarningMessage(char *); +void SendInfoMessage(char *); +void SendTraceMessage(char *); +void SendMessageTo(struct MsgPort *, struct MsgPort *, long); + +#endif diff --git a/net.c b/net.c new file mode 100644 index 0000000..a5970fc --- /dev/null +++ b/net.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "net_getaddrinfo.h" +#include "message.h" + +int errno; +int gai_h_errno; + +#ifdef AOS3 +int h_errno; +#endif + +static const char *bsdSocketName = "bsdsocket.library"; +static const char *openSocketLibraryError = "Cannot open %s %ld.0"; +static const char *openSocketResourceError = "Cannot open %s"; +struct Library *SocketBase = NULL; + +#define BSDLIB_NAME ((STRPTR)bsdSocketName) +#define BSDLIB_REV 03L + +int OpenSocketLibrary(void) +{ + char message[SETTINGMESSAGELEN]; + + if (SocketBase != NULL) + { + return LIB_OK; + } + + if (!(SocketBase = OpenLibrary((STRPTR)BSDLIB_NAME, BSDLIB_REV))) + { + SNPrintf(message, SETTINGMESSAGELEN, openSocketLibraryError, BSDLIB_NAME, BSDLIB_REV); + SendErrorMessage(message); + return LIB_ERROR; + } + + SNPrintf(message, SETTINGMESSAGELEN, "Opened %s", (char *)SocketBase->lib_IdString); + SendTraceMessage(message); + + if (SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (int)&errno, + SBTM_SETVAL(SBTC_HERRNOLONGPTR), (int)&h_errno, TAG_DONE)) + { + SNPrintf(message, SETTINGMESSAGELEN, openSocketResourceError, BSDLIB_NAME); + SendErrorMessage(message); + CloseLibrary(SocketBase); + return LIB_ERROR; + } + + return LIB_OK; +} + +void CloseSocketLibrary(void) +{ + if (SocketBase != NULL) + { + char message[SETTINGMESSAGELEN]; + SNPrintf(message, SETTINGMESSAGELEN, "Closing %s", (char *)SocketBase->lib_IdString); + SendTraceMessage(message); + CloseLibrary(SocketBase); + SocketBase = NULL; + } +} + +const char *GetErrorText(void) +{ + switch (errno) + { + case ENOTSOCK: + return "Socket operation on non-socket"; + case EDESTADDRREQ: + return "Destination address required"; + case EMSGSIZE: + return "Message too long"; + case EPROTOTYPE: + return "Protocol wrong type for socket"; + case ENOPROTOOPT: + return "Protocol not available"; + case EPROTONOSUPPORT: + return "Protocol not supported"; + case ESOCKTNOSUPPORT: + return "Socket type not supported"; + case EOPNOTSUPP: + return "Operation not supported"; + case EPFNOSUPPORT: + return "Protocol family not supported"; + case EAFNOSUPPORT: + return "Address family not supported by protocol family"; + case EADDRINUSE: + return "Address already in use"; + case EADDRNOTAVAIL: + return "Can't assign requested address"; + case ENETDOWN: + return "Network is down"; + case ENETUNREACH: + return "Network is unreachable"; + case ENETRESET: + return "Network dropped connection on reset"; + case ECONNABORTED: + return "Software caused connection abort"; + case ECONNRESET: + return "Connection reset by peer"; + case ENOBUFS: + return "No buffer space available"; + case EISCONN: + return "Socket is already connected"; + case ENOTCONN: + return "Socket is not connected"; + case ESHUTDOWN: + return "Can't send after socket shutdown"; + case ETIMEDOUT: + return "Operation timed out"; + case ECONNREFUSED: + return "Connection refused"; + case EHOSTDOWN: + return "Host is down"; + case EHOSTUNREACH: + return "No route to host"; + default: + return "Socket error"; + } +} + +const char *GetHostErrorText(void) +{ + if (gai_h_errno != 0) + { + return gai_h_errno == EAI_SYSTEM + ? GetErrorText() + : gai_strerror(gai_h_errno); + } + + switch (h_errno) + { + case EAI_BADFLAGS: + return "Invalid value for ai_flags"; + case EAI_NONAME: + return "Name or service is not known"; + case EAI_AGAIN: + return "Temporary failure in name resolution"; + case EAI_FAIL: + return "Non-recoverable failure in name resolution"; + case EAI_FAMILY: + return "ai_family not supported"; + case EAI_SOCKTYPE: + return "ai_socktype not supported"; + case EAI_SERVICE: + return "Service not supported for ai_socktype"; + case EAI_ADDRFAMILY: + return "Address family for name not supported"; + case EAI_MEMORY: + return "Memory allocation failure"; + case EAI_SYSTEM: + return GetErrorText(); + default: + return "DNS error"; + } +} diff --git a/net_getaddrinfo.c b/net_getaddrinfo.c new file mode 100644 index 0000000..147cc16 --- /dev/null +++ b/net_getaddrinfo.c @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2001, 02 Motoyuki Kasahara + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This program provides getaddrinfo() and getnameinfo() described in + * RFC2133, 2553 and 3493. These functions are mainly used for IPv6 + * application to resolve hostname or address. + * + * This program is designed to be working on traditional IPv4 systems + * which don't have those functions. Therefore, this implementation + * supports IPv4 only. + * + * This program is useful for application which should support both IPv6 + * and traditional IPv4 systems. Use genuine getaddrinfo() and getnameinfo() + * provided by system if the system supports IPv6. Otherwise, use this + * implementation. + * + * This program also provides freeaddrinfo() and gai_strerror(). + * + * Restriction: + * getaddrinfo() and getnameinfo() of this program are NOT thread + * safe, unless the cpp macro ENABLE_THREAD is defined. + */ + +#include "config.h" +#include "net_getaddrinfo.h" +#include "mem.h" + +#define gettext(string) (string) +#define _(string) (string) +#define N_(string) (string) + +extern int gai_h_errno; + +#ifdef AOS3 +extern int h_errno; +#endif + +/* + * Error messages for gai_strerror(). + */ +static char *eai_errlist[] = { + N_("Success"), + + /* EAI_ADDRFAMILY */ + N_("Address family for hostname not supported"), + + /* EAI_AGAIN */ + N_("Temporary failure in name resolution"), + + /* EAI_BADFLAGS */ + N_("Invalid value for ai_flags"), + + /* EAI_FAIL */ + N_("Non-recoverable failure in name resolution"), + + /* EAI_FAMILY */ + N_("ai_family not supported"), + + /* EAI_MEMORY */ + N_("Memory allocation failure"), + + /* EAI_NONAME */ + N_("Hostname nor servname provided, or not known"), + + /* EAI_OVERFLOW */ + N_("An argument buffer overflowed"), + + /* EAI_SERVICE */ + N_("servname not supported for ai_socktype"), + + /* EAI_SOCKTYPE */ + N_("ai_socktype not supported"), + + /* EAI_SYSTEM */ + N_("System error returned in errno")}; + +/* + * Default hints for getaddrinfo(). + */ +static struct addrinfo default_hints = { + 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL}; + +/* + * Mutex. + */ +#ifdef ENABLE_THREAD +struct SignalSemaphore getaddrinfoSemaphore; +#endif + +/* + * gai_strerror(). + */ +#if defined(__CLIB2__) +STRPTR gai_strerror(LONG ecode) +#else +const char *gai_strerror(int ecode) +#endif +{ + if (ecode < 0 || ecode > EAI_SYSTEM) + return _("Unknown error"); + + return gettext(eai_errlist[ecode]); +} + +/* + * freeaddrinfo(). + */ +void freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next_ai; + + while (ai != NULL) + { + if (ai->ai_canonname != NULL) + FreeMemSafe(ai->ai_canonname); + if (ai->ai_addr != NULL) + FreeMemSafe(ai->ai_addr); + next_ai = ai->ai_next; + FreeMemSafe(ai); + ai = next_ai; + } +} + +/* + * Return 1 if the string `s' represents an integer. + */ +static int is_integer(const char *s) +{ + if (*s == '-' || *s == '+') + s++; + if (*s < '0' || '9' < *s) + return 0; + + s++; + while ('0' <= *s && *s <= '9') + s++; + + return (*s == '\0'); +} + +/* + * Return 1 if the string `s' represents an IPv4 address. + * Unlike inet_addr(), it doesn't permit malformed nortation such + * as "192.168". + */ +static int +is_address(const char *s) +{ + const static char delimiters[] = {'.', '.', '.', '\0'}; + int i, j; + int octet; + + for (i = 0; i < 4; i++) + { + if (*s == '0' && *(s + 1) != delimiters[i]) + return 0; + for (j = 0, octet = 0; '0' <= *s && *s <= '9' && j < 3; s++, j++) + octet = octet * 10 + (*s - '0'); + if (j == 0 || octet > 255 || *s != delimiters[i]) + return 0; + s++; + } + + return 1; +} + +/* + * getaddrinfo(). + */ +#if defined(__CLIB2__) +LONG getaddrinfo( + STRPTR nodename, + STRPTR servname, + struct addrinfo *hints, + struct addrinfo **res) +#else +int getaddrinfo( + const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res) +#endif +{ + struct addrinfo *head_res = NULL; + struct addrinfo *tail_res = NULL; + struct addrinfo *new_res; + struct sockaddr_in *sa_in; + struct in_addr **addr_list; + struct in_addr *addr_list_buf[2]; + struct in_addr addr_buf; + struct in_addr **ap; + struct servent *servent; + struct hostent *hostent; + const char *canonname = NULL; + in_port_t port; + LONG port_parse; + int saved_h_errno; + int result = 0; + +#ifdef ENABLE_THREAD + ObtainSemaphore(&getaddrinfoSemaphore); +#endif + + saved_h_errno = h_errno; + + if (nodename == NULL && servname == NULL) + { + result = EAI_NONAME; + goto end; + } + + if (hints != NULL) + { + if (hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC) + { + result = EAI_FAMILY; + goto end; + } + if (hints->ai_socktype != SOCK_DGRAM && hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != 0) + { + result = EAI_SOCKTYPE; + goto end; + } + } + else + { + hints = &default_hints; + } + + if (servname != NULL) + { + if (is_integer(servname)) + { + StrToLong((STRPTR)servname, &port_parse); + port = htons(port_parse); + } + else + { + if (hints->ai_flags & AI_NUMERICSERV) + { + result = EAI_NONAME; + goto end; + } + + if (hints->ai_socktype == SOCK_DGRAM) + servent = getservbyname((char *)servname, (char *)"udp"); + else if (hints->ai_socktype == SOCK_STREAM) + servent = getservbyname((char *)servname, (char *)"tcp"); + else if (hints->ai_socktype == 0) + servent = getservbyname((char *)servname, (char *)"tcp"); + else + { + result = EAI_SOCKTYPE; + goto end; + } + + if (servent == NULL) + { + result = EAI_SERVICE; + goto end; + } + port = servent->s_port; + } + } + else + { + port = htons(0); + } + + if (nodename != NULL) + { + if (is_address(nodename)) + { + addr_buf.s_addr = inet_addr(nodename); + addr_list_buf[0] = &addr_buf; + addr_list_buf[1] = NULL; + addr_list = addr_list_buf; + + if (hints->ai_flags & AI_CANONNAME && !(hints->ai_flags & AI_NUMERICHOST)) + { + hostent = gethostbyaddr((char *)&addr_buf, + sizeof(struct in_addr), AF_INET); + if (hostent != NULL) + canonname = hostent->h_name; + else + canonname = nodename; + } + } + else + { + if (hints->ai_flags & AI_NUMERICHOST) + { + result = EAI_NONAME; + goto end; + } + + hostent = gethostbyname(nodename); + if (hostent == NULL) + { + switch (h_errno) + { + case HOST_NOT_FOUND: + case NO_DATA: + result = EAI_NONAME; + goto end; + case TRY_AGAIN: + result = EAI_AGAIN; + goto end; + default: + result = EAI_FAIL; + goto end; + } + } + addr_list = (struct in_addr **)hostent->h_addr_list; + + if (hints->ai_flags & AI_CANONNAME) + canonname = hostent->h_name; + } + } + else + { + if (hints->ai_flags & AI_PASSIVE) + addr_buf.s_addr = htonl(INADDR_ANY); + else + addr_buf.s_addr = htonl(0x7F000001); + addr_list_buf[0] = &addr_buf; + addr_list_buf[1] = NULL; + addr_list = addr_list_buf; + } + + for (ap = addr_list; *ap != NULL; ap++) + { + new_res = (struct addrinfo *)AllocMemSafe(sizeof(struct addrinfo)); + if (new_res == NULL) + { + if (head_res != NULL) + freeaddrinfo(head_res); + result = EAI_MEMORY; + goto end; + } + + new_res->ai_family = PF_INET; + new_res->ai_socktype = hints->ai_socktype; + new_res->ai_protocol = hints->ai_protocol; + new_res->ai_addr = NULL; + new_res->ai_addrlen = sizeof(struct sockaddr_in); + new_res->ai_canonname = NULL; + new_res->ai_next = NULL; + + new_res->ai_addr = (struct sockaddr *) + AllocMemSafe(sizeof(struct sockaddr_in)); + if (new_res->ai_addr == NULL) + { + FreeMemSafe(new_res); + if (head_res != NULL) + freeaddrinfo(head_res); + result = EAI_MEMORY; + goto end; + } + + sa_in = (struct sockaddr_in *)new_res->ai_addr; + memset(sa_in, 0, sizeof(struct sockaddr_in)); + sa_in->sin_family = PF_INET; + sa_in->sin_port = port; + memcpy(&sa_in->sin_addr, *ap, sizeof(struct in_addr)); + + if (head_res == NULL) + head_res = new_res; + else + tail_res->ai_next = new_res; + tail_res = new_res; + } + + if (canonname != NULL && head_res != NULL) + { + head_res->ai_canonname = (char *)AllocMemSafe(StrLen(canonname) + 1); + if (head_res->ai_canonname != NULL) + StrCopy(head_res->ai_canonname, canonname); + } + + *res = head_res; + +end: + h_errno = saved_h_errno; + gai_h_errno = result; +#ifdef ENABLE_THREAD + ReleaseSemaphore(&getaddrinfoSemaphore); +#endif + return result; +} + +#if 0 +/* + * Calcurate length of the string `s', where `s' is set by + * sprintf(s, "%d", n). + */ +static int +itoa_length(int n) +{ + int result = 1; + + if (n < 0) + { + n = -n; + result++; + } + + while (n >= 10) + { + result++; + n /= 10; + } + + return result; +} + +#if defined(__CLIB2__) +LONG getnameinfo( + struct sockaddr *sa, + ULONG salen, + STRPTR node, + ULONG nodelen, + STRPTR serv, + ULONG servlen, + ULONG flags) +#else +int getnameinfo( + const struct sockaddr *sa, + socklen_t salen, + char *node, + socklen_t nodelen, + char *serv, + socklen_t servlen, + int flags) +#endif +{ + const struct sockaddr_in *sa_in = (const struct sockaddr_in *)sa; + struct hostent *hostent; + struct servent *servent; + char *ntoa_address; + int saved_h_errno; + int result = 0; + +#ifdef ENABLE_PTHREAD + pthread_mutex_lock(&gai_mutex); +#endif + + saved_h_errno = h_errno; + + if (sa_in->sin_family != PF_INET) + { + result = EAI_FAMILY; + goto end; + } + else if (node == NULL && serv == NULL) + { + result = EAI_NONAME; + goto end; + } + + if (serv != NULL && servlen > 0) + { + if (flags & NI_NUMERICSERV) + servent = NULL; + else if (flags & NI_DGRAM) + servent = getservbyport(sa_in->sin_port, "udp"); + else + servent = getservbyport(sa_in->sin_port, "tcp"); + + if (servent != NULL) + { + if (servlen <= StrLen(servent->s_name)) + { + result = EAI_OVERFLOW; + goto end; + } + StrCopy(serv, servent->s_name); + } + else + { + if (servlen <= itoa_length(ntohs(sa_in->sin_port))) + { + result = EAI_OVERFLOW; + goto end; + } + LongToStr((long)ntohs(sa_in->sin_port), serv); + } + } + + if (node != NULL && nodelen > 0) + { + if (flags & NI_NUMERICHOST) + hostent = NULL; + else + { + hostent = gethostbyaddr((char *)&sa_in->sin_addr, + sizeof(struct in_addr), AF_INET); + } + if (hostent != NULL) + { + if (nodelen <= StrLen(hostent->h_name)) + { + result = EAI_OVERFLOW; + goto end; + } + StrCopy(node, hostent->h_name); + } + else + { + if (flags & NI_NAMEREQD) + { + result = EAI_NONAME; + goto end; + } +#ifndef __libnix__ + ntoa_address = inet_ntoa(sa_in->sin_addr); +#else + ntoa_address = inet_ntoa((LONG)sa_in->sin_addr.s_addr); +#endif + if (nodelen <= StrLen(ntoa_address)) + { + result = EAI_OVERFLOW; + goto end; + } + StrCopy(node, ntoa_address); + } + } + +end: + h_errno = saved_h_errno; + gai_h_errno = result; +#ifdef ENABLE_PTHREAD + pthread_mutex_unlock(&gai_mutex); +#endif + return result; +} +#endif diff --git a/net_getaddrinfo.h b/net_getaddrinfo.h new file mode 100644 index 0000000..526f85e --- /dev/null +++ b/net_getaddrinfo.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2001, 02 Motoyuki Kasahara + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef GETADDRINFO_H_INCLUDED +#define GETADDRINFO_H_INCLUDED + +#include "config.h" + +/* + * Undefine all the macros. + * might defines some of them. + */ +#ifdef EAI_ADDRFAMILY +#undef EAI_ADDRFAMILY +#endif +#ifdef EAI_AGAIN +#undef EAI_AGAIN +#endif +#ifdef EAI_BADFLAGS +#undef EAI_BADFLAGS +#endif +#ifdef EAI_FAIL +#undef EAI_FAIL +#endif +#ifdef EAI_FAMILY +#undef EAI_FAMILY +#endif +#ifdef EAI_MEMORY +#undef EAI_MEMORY +#endif +#ifdef EAI_NONAME +#undef EAI_NONAME +#endif +#ifdef EAI_OVERFLOW +#undef EAI_OVERFLOW +#endif +#ifdef EAI_SERVICE +#undef EAI_SERVICE +#endif +#ifdef EAI_SOCKTYPE +#undef EAI_SOCKTYPE +#endif +#ifdef EAI_SYSTEM +#undef EAI_SYSTEM +#endif + +#ifdef AI_PASSIVE +#undef AI_PASSIVE +#endif +#ifdef AI_CANONNAME +#undef AI_CANONNAME +#endif +#ifdef AI_NUMERICHOST +#undef AI_NUMERICHOST +#endif +#ifdef AI_NUMERICSERV +#undef AI_NUMERICSERV +#endif +#ifdef AI_V4MAPPED +#undef AI_V4MAPPED +#endif +#ifdef AI_ALL +#undef AI_ALL +#endif +#ifdef AI_ADDRCONFIG +#undef AI_ADDRCONFIG +#endif +#ifdef AI_DEFAULT +#undef AI_DEFAULT +#endif + +#ifdef NI_NOFQDN +#undef NI_NOFQDN +#endif +#ifdef NI_NUMERICHOST +#undef NI_NUMERICHOST +#endif +#ifdef NI_NAMEREQD +#undef NI_NAMEREQD +#endif +#ifdef NI_NUMERICSERV +#undef NI_NUMERICSERV +#endif +#ifdef NI_NUMERICSCOPE +#undef NI_NUMERICSCOPE +#endif + +#ifdef NI_DGRAM +#undef NI_DGRAM +#endif +#ifdef NI_MAXHOST +#undef NI_MAXHOST +#endif +#ifdef NI_MAXSERV +#undef NI_MAXSERV +#endif + +/* + * Undefine Roadshow macros. + */ +#ifdef gai_strerror +#undef gai_strerror +#endif +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif +#ifdef getaddrinfo +#undef getaddrinfo +#endif +#ifdef getnameinfo +#undef getnameinfo +#endif + +/* + * Error codes. + */ +#define EAI_ADDRFAMILY 1 +#define EAI_AGAIN 2 +#define EAI_BADFLAGS 3 +#define EAI_FAIL 4 +#define EAI_FAMILY 5 +#define EAI_MEMORY 6 +#define EAI_NONAME 7 +#define EAI_OVERFLOW 8 +#define EAI_SERVICE 9 +#define EAI_SOCKTYPE 10 +#define EAI_SYSTEM 11 + +/* + * Flags for getaddrinfo(). + */ +#define AI_ADDRCONFIG 0x0001 +#define AI_ALL 0x0002 +#define AI_CANONNAME 0x0004 +#define AI_NUMERICHOST 0x0008 +#define AI_NUMERICSERV 0x0010 +#define AI_PASSIVE 0x0020 +#define AI_V4MAPPED 0x0040 +#define AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG) + +/* + * Flags for getnameinfo(). + */ +#define NI_DGRAM 0x0001 +#define NI_NAMEREQD 0x0002 +#define NI_NOFQDN 0x0004 +#define NI_NUMERICHOST 0x0008 +#define NI_NUMERICSCOPE 0x0010 +#define NI_NUMERICSERV 0x0020 + +/* + * Maximum length of FQDN and service name for getnameinfo(). + */ +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 + +/* + * Address families and Protocol families. + */ +#ifndef AF_UNSPEC +#define AF_UNSPEC AF_INET +#endif +#ifndef PF_UNSPEC +#define PF_UNSPEC PF_INET +#endif + +#if defined(AROS) +typedef uint16_t in_port_t; +#endif + +#if defined(__CLIB2__) +STRPTR gai_strerror(LONG); +LONG getaddrinfo(STRPTR, STRPTR, struct addrinfo *, struct addrinfo **); +LONG getnameinfo(struct sockaddr *, ULONG, STRPTR, ULONG, STRPTR, ULONG, ULONG); +#else +const char *gai_strerror(int); +int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); +int getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); +#endif + +void freeaddrinfo(struct addrinfo *); + +#endif diff --git a/net_poll.c b/net_poll.c new file mode 100644 index 0000000..5b80bac --- /dev/null +++ b/net_poll.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#if !defined(HAVE_POLL) +#if defined(__CLIB2__) +#include +#endif + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + unsigned int i; + int maxfds = -1, ret; + fd_set readSet, writeSet, errorSet; + struct timeval timeout_tv, *tvp = NULL; + + if (timeout >= 0) + { + timeout_tv.tv_secs = (timeout / 1000); + timeout_tv.tv_micro = (timeout % 1000) * 1000; + tvp = &timeout_tv; + } + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errorSet); + + for (i = 0; i < nfds; i++) + { + fds[i].revents = 0; + + if (fds[i].events == 0) + continue; + + if (fds[i].fd > maxfds) + maxfds = fds[i].fd; + + if (fds[i].events & POLLIN) + FD_SET(fds[i].fd, &readSet); + } + + ret = WaitSelect(maxfds + 1, &readSet, &writeSet, &errorSet, tvp, NULL); + + if (ret == -1) + return ret; + + for (i = 0; i < nfds; i++) + { + if (fds[i].events == 0) + continue; + + if (FD_ISSET(fds[i].fd, &readSet)) + fds[i].revents |= POLLIN; + } + + return ret; +} +#endif \ No newline at end of file diff --git a/sntp.c b/sntp.c new file mode 100644 index 0000000..0b54c42 --- /dev/null +++ b/sntp.c @@ -0,0 +1,466 @@ +/*- + * Copyright (c) 2007 TANDBERG Telecom AS + * Copyright (c) 2008-2009 Dag-Erling Smørgrav + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "config.h" +#include "time.h" +#include "sntp.h" +#include "mem.h" +#include "net_getaddrinfo.h" +#include "message.h" + +/* + * Convert a struct timeval to an NTP timestamp + */ +void tv2nt(struct timeval *tv, struct ntptime *nt) +{ + uint64_t frac; + + nt->sec = tv->tv_secs + UNIX_EPOCH; + frac = tv->tv_micro; + frac <<= 32; + frac /= 1000 * 1000; + nt->frac = frac; +} + +/* + * Convert an NTP timestamp to a struct timeval + */ +void nt2tv(struct ntptime *nt, struct timeval *tv) +{ + uint64_t frac; + + tv->tv_sec = nt->sec - UNIX_EPOCH; + frac = nt->frac; + frac *= 1000 * 1000; + frac >>= 32; + tv->tv_usec = frac; +} + +/* + * Convert struct ntptime in-place from network to host order + */ +void n2h_ntp(struct ntptime *nt) +{ + nt->sec = ntohl(nt->sec); + nt->frac = ntohl(nt->frac); +} + +/* + * Convert struct ntptime in-place from host to network order + */ +void h2n_ntp(struct ntptime *nt) +{ + nt->sec = htonl(nt->sec); + nt->frac = htonl(nt->frac); +} + +/* + * SNTP client state + */ +struct sntp +{ + /* parameters from sntp_create() */ + char *srcaddr; + char *srcport; + char *dstaddr; + char *dstport; + + /* DNS data */ + int family; + int socktype; + int protocol; + struct sockaddr *laddr; + socklen_t laddrlen; + struct sockaddr *raddr; + socklen_t raddrlen; + + /* socket and poll structure */ + int sd; + struct pollfd pfd; + + /* protocol state */ + struct ntptime last_send; + struct ntptime last_recv; +}; + +/* + * Initialize an SNTP client context + * + * Multiple contexts can coexist as long as they do not use the same + * source port. + * + * NOTICE: Consider adding support for binding to a specific source address. + */ +struct sntp * +sntp_create(const char *dstaddr, const char *dstport, + const char *srcaddr, const char *srcport) +{ + struct sntp *sntp; + sntp = AllocMemSafe(sizeof(struct sntp)); + sntp->sd = -1; + + sntp->srcaddr = srcaddr ? StrDupSafe(srcaddr) : NULL; + sntp->srcport = StrDupSafe(srcport ? srcport : "123"); + sntp->dstaddr = StrDupSafe(dstaddr); + sntp->dstport = StrDupSafe(dstport ? dstport : "123"); + + /* good to go */ + return (sntp); +} + +/* + * Look up local and remote addresses and set up the socket + */ +int sntp_open(struct sntp *sntp) +{ + struct addrinfo hints; + struct addrinfo *aiv, *ai; + int ret; + + if (sntp->sd != -1) + return (SNTP_OK); + + /* resolve the server address */ + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + if ((ret = getaddrinfo(sntp->dstaddr, sntp->dstport, &hints, &aiv)) != 0) + return (SNTP_DNSERR); + + /* Something went wrong */ + if (aiv == NULL) + return (SNTP_SYSERR); + + /* + * Iterate over the results until we find one we can use. This is + * sometimes necessary on systems with partial IPv6 support, where + * the resolver may return IPv6 addresses which the network stack + * can't handle. + */ + for (ai = aiv; ai; ai = ai->ai_next) + { + sntp->sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sntp->sd >= 0) + break; + } + + if (sntp->sd == -1) + { + SendWarningMessage("Could not get socket descriptor"); + freeaddrinfo(aiv); + return (SNTP_SYSERR); + } + + sntp->raddr = MemDupSafe(ai->ai_addr, ai->ai_addrlen); + sntp->raddrlen = ai->ai_addrlen; + sntp->family = ai->ai_family; + sntp->socktype = ai->ai_socktype; + sntp->protocol = ai->ai_protocol; + freeaddrinfo(aiv); + + /* get a matching local address */ + memset(&hints, 0, sizeof hints); + hints.ai_family = sntp->family; + hints.ai_socktype = sntp->socktype; + hints.ai_protocol = sntp->protocol; + hints.ai_flags = AI_PASSIVE; + if ((ret = getaddrinfo(sntp->srcaddr, sntp->srcport, &hints, &aiv)) != 0) + { + sntp_close(sntp); + return (SNTP_DNSERR); + } + + /* Something went wrong */ + if (aiv == NULL) + return (SNTP_SYSERR); + + /* NOTICE: Consider asserting that results match expectations */ + sntp->laddr = MemDupSafe(aiv->ai_addr, aiv->ai_addrlen); + sntp->laddrlen = aiv->ai_addrlen; + freeaddrinfo(aiv); + + /* prepare our socket */ + /* NOTICE: Sometimes AmigaOS do not like bindings. Just leave it for now. + if (bind(sntp->sd, sntp->laddr, sntp->laddrlen) != 0) { + LogError("Could not bind socket"); + sntp_close(sntp); + return (SNTP_SYSERR); + } + */ + + if (connect(sntp->sd, sntp->raddr, sntp->raddrlen) != 0) + { + SendWarningMessage("Could not connect socket"); + sntp_close(sntp); + return (SNTP_SYSERR); + } + + /* prepare our pollfd */ + sntp->pfd.fd = sntp->sd; + sntp->pfd.events = POLLIN; + sntp->pfd.revents = 0; + + return (SNTP_OK); +} + +/* + * Destroy an SNTP client context + * + * This is called several times during error handling in other parts of + * the code, so we should save and restore errno + */ +void sntp_close(struct sntp *sntp) +{ + int serrno; + + serrno = errno; + + sntp->family = 0; + sntp->socktype = 0; + sntp->protocol = 0; + + if (sntp->laddr) + FreeMemSafe(sntp->laddr); + sntp->laddrlen = 0; + + if (sntp->raddr) + FreeMemSafe(sntp->raddr); + sntp->raddrlen = 0; + + if (sntp->sd != -1) + CloseSocket(sntp->sd); + memset(&sntp->pfd, 0, sizeof sntp->pfd); + + nt_zero(sntp->last_send); + nt_zero(sntp->last_recv); + + errno = serrno; +} + +/* + * Destroy an SNTP client context + */ +void sntp_destroy(struct sntp *sntp) +{ + (void)sntp_close(sntp); + + if (sntp->srcaddr) + FreeMemSafe(sntp->srcaddr); + sntp->srcaddr = 0; + + if (sntp->srcport) + FreeMemSafe(sntp->srcport); + sntp->srcport = 0; + + if (sntp->dstaddr) + FreeMemSafe(sntp->dstaddr); + sntp->dstaddr = 0; + + if (sntp->dstport) + FreeMemSafe(sntp->dstport); + sntp->dstport = 0; + + FreeMemSafe(sntp); +} + +/* + * Structure of an NTP message without authenticator + */ +struct ntp_msg +{ + uint8_t flags; + uint8_t stratum; + uint8_t poll; + uint8_t precision; + uint32_t root_delay; + uint32_t root_dispersion; + uint8_t reference_id[4]; + struct ntptime reference; + struct ntptime originate; + struct ntptime receive; + struct ntptime transmit; +}; + +/* + * Send an SNTP request + */ +sntp_err_t +sntp_send(struct sntp *sntp) +{ + struct timeval tv; + struct ntp_msg msg; + ssize_t ret; + sntp_err_t se; + + if ((se = sntp_open(sntp)) != SNTP_OK) + return (se); + + memset(&msg, 0, sizeof msg); + msg.flags = 0x23; /* version 4, client */ + + GetTimeOfDay(&tv); + tv2nt(&tv, &msg.transmit); + + h2n_ntp(&msg.transmit); + ret = send(sntp->sd, (void *)&msg, sizeof(struct ntp_msg), 0); + + if (ret < 0) + return (SNTP_SYSERR); + + tv2nt(&tv, &sntp->last_send); + + return (SNTP_OK); +} + +/* + * Have we sent a request to which we're still expecting a response? + */ +sntp_err_t +sntp_pending(struct sntp *sntp) +{ + + /* not currently open */ + if (sntp->sd == -1) + return (SNTP_NOREQ); + + /* last request predates last response */ + if (nt_lt(sntp->last_send, sntp->last_recv)) + return (SNTP_NOREQ); + + return (SNTP_OK); +} + +/* + * Poll for the arrival of an SNTP reply + */ +sntp_err_t +sntp_poll(struct sntp *sntp, int timeout) +{ + sntp_err_t se; + + if ((se = sntp_pending(sntp)) != SNTP_OK) + return (se); + + switch (poll(&sntp->pfd, 1, timeout)) + { + case -1: + sntp_close(sntp); + return (SNTP_SYSERR); + case 0: + return (SNTP_NORESP); + case 1: + return (SNTP_OK); + } + + return 0; +} + +/* + * Receive and process an SNTP reply + */ +sntp_err_t +sntp_recv(struct sntp *sntp, struct ntptime *nt) +{ + struct timeval tv; + struct ntp_msg msg; + sntp_err_t se; + int res; + + if ((se = sntp_pending(sntp)) != SNTP_OK) + return (se); + + /* NOTICE: Consider using recvmsg() instead */ + res = recv(sntp->sd, (void *)&msg, sizeof(struct ntp_msg), MSG_DONTWAIT); + switch (res) + { + case -1: + if (errno == EAGAIN) + return (SNTP_NORESP); + sntp_close(sntp); + return (SNTP_SYSERR); + case 0: + /* can this actually occur? */ + return (SNTP_NORESP); + case sizeof msg: + /* good! */ + break; + default: + /* we got something, but bob knows what */ + return (SNTP_BADRESP); + } + + /* record time of arrival */ + GetTimeOfDay(&tv); + + /* convert to host order */ + n2h_ntp(&msg.originate); + n2h_ntp(&msg.receive); + n2h_ntp(&msg.transmit); + + /* look for kiss packet */ + if (msg.flags == 0xe4 && msg.stratum == 0) + { + char message[SETTINGMESSAGELEN]; + SNPrintf(message, SETTINGMESSAGELEN, + "KoD: %.4s", msg.reference_id); + SendWarningMessage(message); + /* NOTICE: Consider taking a closer look at the kiss code */ + return (SNTP_BACKOFF); + } + + /* check validity: synchronized NTPv4 server */ + switch (msg.flags) + { + case 0x23: /* version 4 client */ + /* we're probably accidentally querying ourselves */ + return (SNTP_BADRESP); + + case 0x24: /* no warning, version 4, server */ + case 0x64: /* subtract leap second, version 4, server */ + case 0xa4: /* add leap second, version 4, server */ + /* these are the normal, useful cases */ + break; + + case 0xe4: /* unsynchronized, version 4, server */ + /* server not usable (yet?) */ + return (SNTP_LAME); + + default: + return (SNTP_BADRESP); + } + + /* check if this is the response we were expecting */ + if (!nt_eq(msg.originate, sntp->last_send)) + /* probably delayed response to old request */ + return (SNTP_NORESP); + + tv2nt(&tv, &sntp->last_recv); + *nt = msg.transmit; + return (SNTP_OK); +} diff --git a/sntp.h b/sntp.h new file mode 100644 index 0000000..41c6213 --- /dev/null +++ b/sntp.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2007 TANDBERG Telecom AS + * Copyright (c) 2008-2009 Dag-Erling Smørgrav + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef SNTP_H_INCLUDED +#define SNTP_H_INCLUDED + +#include "config.h" + +struct sntp; + +/* + * Structure of an NTP timestamp + */ +struct ntptime +{ + uint32_t sec; + uint32_t frac; +}; + +/* + * Useful epochs, in seconds since NTP epoch + */ +#define UNIX_EPOCH 2208988800UL /* 1970-01-01 00:00:00 UTC */ +#define UTC_EPOCH 2272060800UL /* 1972-01-01 00:00:00 UTC */ + +/* clear a struct ntptime */ +#define nt_zero(nt) \ + (void)((nt).sec = (nt).frac = 0) + +/* comparison macros */ +#define nt_cmp(nt1, op, nt2) \ + ((nt1).sec op(nt2).sec && (nt1).frac op(nt2).frac) +#define nt_lt(nt1, nt2) \ + nt_cmp(nt1, <, nt2) +#define nt_le(nt1, nt2) \ + nt_cmp(nt1, <=, nt2) +#define nt_eq(nt1, nt2) \ + nt_cmp(nt1, ==, nt2) +#define nt_ge(nt1, nt2) \ + nt_cmp(nt1, >=, nt2) +#define nt_gt(nt1, nt2) \ + nt_cmp(nt1, >, nt2) + +/* + * Conversion functions + */ +void tv2nt(struct timeval *, struct ntptime *); +void nt2tv(struct ntptime *, struct timeval *); +void h2n_nt(struct ntptime *); +void n2h_nt(struct ntptime *); + +/* + * Error codes + */ +typedef enum sntp_err +{ + SNTP_OK, /* fine */ + SNTP_SYSERR, /* check errno */ + SNTP_DNSERR, /* dns error */ + SNTP_NOREQ, /* no request sent */ + SNTP_NORESP, /* no response received */ + SNTP_BADRESP, /* invalid response received */ + SNTP_LAME, /* server is lame / unsynchronized */ + SNTP_BACKOFF, /* polling too frequently */ +} sntp_err_t; + +/* + * SNTP client + */ +struct sntp *sntp_create(const char *, const char *, const char *, const char *); +int sntp_open(struct sntp *); +void sntp_close(struct sntp *); +void sntp_destroy(struct sntp *); +sntp_err_t sntp_send(struct sntp *); +sntp_err_t sntp_pending(struct sntp *); +sntp_err_t sntp_poll(struct sntp *, int); +sntp_err_t sntp_recv(struct sntp *, struct ntptime *); + +#endif diff --git a/state.c b/state.c new file mode 100644 index 0000000..781790f --- /dev/null +++ b/state.c @@ -0,0 +1,880 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "state.h" +#include "mem.h" + +struct AppState *Globals; +struct AppSettingKeys *SettingKeys; + +static const struct AppSettingKeys SettingKeyStruct = { + .DestinationAddress = KEYWORD_SERVER, + .DestinationPort = KEYWORD_PORT, + .Threshold = KEYWORD_THRESHOLD, + .Interval = KEYWORD_INTERVAL, + .Priority = KEYWORD_PRIORITY, + .Readonly = KEYWORD_READONLY, + .Timeout = KEYWORD_TIMEOUT, + .Verbose = KEYWORD_VERBOSE, + .LogFile = KEYWORD_LOGFILE}; + +const struct AppSettings DefaultSettings = { + .Type = DefaultSettingType, + .DestinationAddress = (char *)SERVER_DEF, + .DestinationPort = (char *)PORT_DEF, + .Timeout = TIMEOUT_DEF, + .Interval = INTERVAL_DEF, + .Verbose = VERBOSE_DEF, + .Readonly = READONLY_DEF, + .Priority = PRIORITY_DEF, + .Threshold = THRESHOLD_DEF, + .LogFile = (char *)LOGFILE_DEF, + .Values = 0xFFFF}; + +static void SetPrioritySetting(struct AppSettings *, void *); +static void SetIntervalSetting(struct AppSettings *, void *); +static void SetVerboseSetting(struct AppSettings *, void *); +static void SetTimeoutSetting(struct AppSettings *, void *); +static void SetThresholdSetting(struct AppSettings *, void *); +static void SetDestinationAddressSetting(struct AppSettings *, void *); +static void SetDestinationPortSetting(struct AppSettings *, void *); +static void SetReadOnlySetting(struct AppSettings *, void *); +static void SetLogFileSetting(struct AppSettings *, void *); + +// Keyword order in settingFunctions needs to match order in keyword template +const struct SettingFunc settingFunctions[] = { + {KEYWORD_READONLY, SetReadOnlySetting}, + {KEYWORD_SERVER, SetDestinationAddressSetting}, + {KEYWORD_PORT, SetDestinationPortSetting}, + {KEYWORD_TIMEOUT, SetTimeoutSetting}, + {KEYWORD_THRESHOLD, SetThresholdSetting}, + {KEYWORD_INTERVAL, SetIntervalSetting}, + {KEYWORD_VERBOSE, SetVerboseSetting}, + {KEYWORD_PRIORITY, SetPrioritySetting}, + {KEYWORD_LOGFILE, SetLogFileSetting}}; + +static const char *prefsFile = "ENV:timekeeper.prefs"; +static const char *persistentPrefsFile = "ENVARC:timekeeper.prefs"; +static const char *prefsFileSearch = "Searching for preference in %s"; +static const char *prefsFileFound = "Found preference file"; +static const char *prefsFileNotFound = "Preference file not found"; +static const char *prefsFileSave = "Saving preferences in %s"; +static const char *fileOpenError = "Could not open preference file"; +static const char *fileSaveError = "Could not save preference file"; +static const char *fileReadError = "Error while reading file"; +static const char *fileWriteError = "Error while writing file"; +static const char *unknownSetting = "Found unknown setting in preference file: %s"; +static const char *foundSetting = "Found %s in preference file"; +static const char *foundWbSetting = "Found tooltype from icon: %s"; +static const char *foundCliSetting = "Got %s from CLI"; +static const char *integerError = "Value should be an integer: %s"; +static const char *applyDefaultSettings = "Applying default values"; +static const char *applyFileSettings = "Applying values from preference file"; +static const char *applyCliSettings = "Applying values from CLI"; +static const char *applyWbSettings = "Applying values from tooltypes"; +static const char *effectiveSettings = "Listing runtime values"; +static const char *noLogFile = "(none)"; +static const char *settingTooLow = "%s < %ld (too low)"; +static const char *settingTooHigh = "%s > %ld (too high)"; +static const char *settingGreaterThan = "%s > %s"; +static const char *settingChangedLong = "%s changed: %ld -> %ld"; +static const char *settingChangedString = "%s changed: %s -> %s"; +static const char *settingSetLong = "%s already set to %ld"; +static const char *settingSetString = "%s already set to %s"; +static const char *settingValueLong = "%s=%ld"; +static const char *settingValueString = "%s=%s"; +static const char *saveValueLong = "%s=%ld\n"; +static const char *saveValueString = "%s=%s\n"; + +#define MAXSETTINGLINELEN 256 + +static struct AppSettings *fileSettings; +static struct AppSettings *cachedSettings; + +void InitState(void) +{ + Globals = (struct AppState *)AllocMemSafe(sizeof(struct AppState)); + Globals->Broker = (struct AppBroker *)AllocMemSafe(sizeof(struct AppBroker)); + Globals->Window = (struct AppSettingWindow *)AllocMemSafe(sizeof(struct AppSettingWindow)); + Globals->Settings = CreateSettings(GlobalSettingType); + Globals->Locale = OpenLocale(NULL); + SettingKeys = (struct AppSettingKeys *)&SettingKeyStruct; +} + +void DestroyState(void) +{ + if (Globals->Syncer != NULL) + { + FreeMemSafe(Globals->Syncer); + Globals->Syncer = NULL; + } + + FreeSettings(Globals->Settings); + CloseLocale(Globals->Locale); + FreeMemSafe((void *)Globals->Window); + FreeMemSafe((void *)Globals->Broker); + FreeMemSafe((void *)Globals); +} + +void ShowAppSettings(struct AppSettings *settings) +{ + char low[MAXLONGLONGCHARSIZE]; + + LongLongToStr(settings->Threshold, low); + LogTrace(settingValueLong, SettingKeys->Priority, settings->Priority); + LogTrace(settingValueString, SettingKeys->Threshold, low); + LogTrace(settingValueString, SettingKeys->DestinationAddress, settings->DestinationAddress); + LogTrace(settingValueString, SettingKeys->DestinationPort, settings->DestinationPort); + LogTrace(settingValueLong, SettingKeys->Timeout, settings->Timeout); + LogTrace(settingValueLong, SettingKeys->Interval, settings->Interval); + LogTrace(settingValueLong, SettingKeys->Verbose, settings->Verbose); + LogTrace(settingValueLong, SettingKeys->Readonly, settings->Readonly); + LogTrace(settingValueString, SettingKeys->LogFile, + settings->LogFile ? settings->LogFile : noLogFile); +} + +void ShowSettings(void) +{ + LogTrace(effectiveSettings); + ShowAppSettings(Globals->Settings); +} + +void LogFoundSetting(long type, const char *name) +{ + switch (type) + { + case FileSettingType: + LogTrace(foundSetting, name); + break; + case CliSettingType: + LogTrace(foundCliSetting, name); + break; + case WbSettingType: + LogTrace(foundWbSetting, name); + break; + default: + break; + } +} + +static void SetPrioritySetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Priority); + if (setting->Type == CliSettingType) + { + setting->Priority = *(long *)value; + setting->Values |= PrioritySet; + return; + } + + if (TryParseLong((char *)value, &setting->Priority)) + { + setting->Values |= PrioritySet; + return; + } + + LogWarning(integerError, value); +} + +static void SetVerboseSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Verbose); + if (setting->Type == CliSettingType) + { + setting->Verbose = *(long *)value; + setting->Values |= VerboseSet; + return; + } + + if (TryParseLong((char *)value, &setting->Verbose)) + { + setting->Values |= VerboseSet; + return; + } + + LogWarning(integerError, value); +} + +static void SetIntervalSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Interval); + if (setting->Type == CliSettingType) + { + setting->Interval = *(long *)value; + setting->Values |= IntervalSet; + return; + } + + if (TryParseLong((char *)value, &setting->Interval)) + { + setting->Values |= IntervalSet; + return; + } + + LogWarning(integerError, value); +} + +static void SetTimeoutSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Timeout); + if (setting->Type == CliSettingType) + { + setting->Timeout = *(long *)value; + setting->Values |= TimeoutSet; + return; + } + + if (TryParseLong((char *)value, &setting->Timeout)) + { + setting->Values |= TimeoutSet; + return; + } + + LogWarning(integerError, value); +} + +static void SetThresholdSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Threshold); + if (TryParseLongLong((char *)value, &setting->Threshold)) + { + setting->Values |= ThresholdSet; + return; + } + + LogWarning(integerError, value); +} + +static void SetDestinationAddressSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->DestinationAddress); + setting->DestinationAddress = StrDupSafe((const char *)value); + setting->Values |= DestinationAddressSet; +} + +static void SetDestinationPortSetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->DestinationPort); + setting->DestinationPort = StrDupSafe((const char *)value); + setting->Values |= DestinationPortSet; +} + +static void SetReadOnlySetting(struct AppSettings *setting, void *value) +{ + LogFoundSetting(setting->Type, SettingKeys->Readonly); + if (setting->Type == CliSettingType) + { + setting->Readonly = value != 0 ? true : false; + setting->Values |= ReadonlySet; + return; + } + + if (TryParseLong((char *)value, &setting->Readonly)) + { + setting->Readonly = setting->Readonly != 0 ? true : false; + setting->Values |= ReadonlySet; + return; + } + + LogWarning(integerError, value); +} + +static void SetLogFileSetting(struct AppSettings *setting, void *value) +{ + static const char *no = "no"; + LogFoundSetting(setting->Type, SettingKeys->LogFile); + + if (value == NULL || *((const char *)value) == '\0' || + Stricmp((STRPTR)no, (STRPTR)value) == 0 || + Stricmp((STRPTR)noLogFile, (STRPTR)value) == 0) + { + setting->LogFile = NULL; + } + else + { + setting->LogFile = StrDupSafe((const char *)value); + } + setting->Values |= LogFileSet; +} + +static void ParseSetting(struct AppSettings *settings, char *line) +{ + char *value; + char *end; + int i; + + value = StrChr(line, '=', MAXSETTINGLINELEN); + if (value == NULL) + { + LogWarning(unknownSetting, "No assignment"); + return; + } + + end = StrChr(value, '\n', MAXSETTINGLINELEN); + if (end == NULL) + { + LogWarning(unknownSetting, "No end of line"); + return; + } + + *value++ = '\0'; + *end = '\0'; + + for (i = 0; i < KEYWORD_COUNT; i++) + { + if (Stricmp((STRPTR)settingFunctions[i].Name, (STRPTR)line) == 0) + { + settingFunctions[i].Function(settings, (void *)value); + return; + } + } + + LogWarning(unknownSetting, line); +} + +void LoadSettings(void) +{ + struct AppSettings *settings; + const int maxLines = 25; + char line[MAXSETTINGLINELEN]; + char message[MAXDOSERRORLEN]; + bool eof = false; + int count = 0; + long error; + BPTR file; + + LogTrace(prefsFileSearch, prefsFile); + file = Open((STRPTR)prefsFile, MODE_OLDFILE); + + if (!file) + { + error = IoErr(); + if (error == ERROR_OBJECT_NOT_FOUND) + { + LogWarning(prefsFileNotFound); + } + else + { + Fault(error, (STRPTR)fileOpenError, (STRPTR)message, MAXDOSERRORLEN); + LogWarning(message); + } + return; + } + + LogInfo(prefsFileFound); + + settings = CreateSettings(FileSettingType); + + do + { + char *c = (char *)FGets(file, (STRPTR)line, MAXSETTINGLINELEN); + eof = (c == NULL); + + if (!eof) + { + ParseSetting(settings, line); + count++; + } + } while (!eof && count < maxLines); + + // If NULL is returned for an EOF, IoErr() will return 0. + error = IoErr(); + if (error != 0) + { + Fault(error, (STRPTR)fileReadError, (STRPTR)message, MAXDOSERRORLEN); + LogError(message); + } + + Close(file); + fileSettings = settings; +} + +static void WriteSetting(BPTR file, const char *format, ...) +{ + long count; + va_list args; + va_start(args, format); + count = VFPrintf(file, (void *)format, (void *)args); + va_end(args); + + if (count <= 0) + { + long error = IoErr(); + if (error != 0) + { + char message[MAXDOSERRORLEN]; + Fault(error, (STRPTR)fileWriteError, (STRPTR)message, MAXDOSERRORLEN); + LogError(message); + } + } +} + +void SaveSettings(bool persist) +{ + char low[MAXLONGLONGCHARSIZE]; + const char *fileName = persist ? persistentPrefsFile : prefsFile; + BPTR file = Open((STRPTR)fileName, MODE_NEWFILE); + if (!file) + { + char message[MAXDOSERRORLEN]; + long error = IoErr(); + Fault(error, (STRPTR)fileSaveError, (STRPTR)message, MAXDOSERRORLEN); + LogWarning(message); + return; + } + + LogInfo(prefsFileSave, fileName); + + LongLongToStr(Globals->Settings->Threshold, low); + WriteSetting(file, saveValueLong, SettingKeys->Priority, Globals->Settings->Priority); + WriteSetting(file, saveValueString, SettingKeys->Threshold, low); + WriteSetting(file, saveValueString, SettingKeys->DestinationAddress, Globals->Settings->DestinationAddress); + WriteSetting(file, saveValueString, SettingKeys->DestinationPort, Globals->Settings->DestinationPort); + WriteSetting(file, saveValueLong, SettingKeys->Timeout, Globals->Settings->Timeout); + WriteSetting(file, saveValueLong, SettingKeys->Interval, Globals->Settings->Interval); + WriteSetting(file, saveValueLong, SettingKeys->Verbose, Globals->Settings->Verbose); + WriteSetting(file, saveValueLong, SettingKeys->Readonly, Globals->Settings->Readonly); + WriteSetting(file, saveValueString, SettingKeys->LogFile, Globals->Settings->LogFile); + + Close(file); +} + +struct AppSettings *CreateSettings(long type) +{ + struct AppSettings *settings; + settings = (struct AppSettings *)AllocMemSafe(sizeof(struct AppSettings)); + settings->Type = type; + return settings; +} + +struct AppSettings *CopySettings(const struct AppSettings *settings) +{ + struct AppSettings *s = CreateSettings(settings->Type); + CopyMem((void *)settings, s, sizeof(struct AppSettings)); + s->DestinationAddress = StrDupSafe(settings->DestinationAddress); + s->DestinationPort = StrDupSafe(settings->DestinationPort); + if (s->LogFile != NULL) + { + s->LogFile = StrDupSafe(settings->LogFile); + } + s->Values = 0xFFFF; + return s; +} + +void FreeSettings(struct AppSettings *settings) +{ + if (settings->DestinationAddress != NULL) + { + FreeMemSafe(settings->DestinationAddress); + } + + if (settings->DestinationPort != NULL) + { + FreeMemSafe(settings->DestinationPort); + } + + if (settings->LogFile != NULL) + { + FreeMemSafe(settings->LogFile); + } + + FreeMemSafe(settings); +} + +void ApplyAppSettings(struct AppSettings *settings, bool quiet) +{ + switch (settings->Type) + { + case DefaultSettingType: + LogInfo(applyDefaultSettings); + break; + case FileSettingType: + LogInfo(applyFileSettings); + break; + case CliSettingType: + LogInfo(applyCliSettings); + break; + case WbSettingType: + LogInfo(applyWbSettings); + break; + default: + break; + } + + if ((settings->Values & PrioritySet) == PrioritySet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueLong, SettingKeys->Priority, settings->Priority); + } + else if (Globals->Settings->Priority != settings->Priority) + { + LogInfo(settingChangedLong, SettingKeys->Priority, Globals->Settings->Priority, + settings->Priority); + } + else if (!quiet) + { + LogTrace(settingSetLong, SettingKeys->Priority, settings->Priority); + } + Globals->Settings->Priority = settings->Priority; + } + + if ((settings->Values & VerboseSet) == VerboseSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueLong, SettingKeys->Verbose, settings->Verbose); + } + else if (Globals->Settings->Verbose != settings->Verbose) + { + LogInfo(settingChangedLong, SettingKeys->Verbose, Globals->Settings->Verbose, + settings->Verbose); + } + else if (!quiet) + { + LogTrace(settingSetLong, SettingKeys->Verbose, settings->Verbose); + } + Globals->Settings->Verbose = settings->Verbose; + } + + if ((settings->Values & TimeoutSet) == TimeoutSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueLong, SettingKeys->Timeout, settings->Timeout); + } + else if (Globals->Settings->Timeout != settings->Timeout) + { + LogInfo(settingChangedLong, SettingKeys->Timeout, Globals->Settings->Timeout, + settings->Timeout); + } + else if (!quiet) + { + LogTrace(settingSetLong, SettingKeys->Timeout, settings->Timeout); + } + Globals->Settings->Timeout = settings->Timeout; + } + + if ((settings->Values & IntervalSet) == IntervalSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueLong, SettingKeys->Interval, settings->Interval); + } + else if (Globals->Settings->Interval != settings->Interval) + { + LogInfo(settingChangedLong, SettingKeys->Interval, Globals->Settings->Interval, + settings->Interval); + } + else if (!quiet) + { + LogTrace(settingSetLong, SettingKeys->Interval, settings->Interval); + } + Globals->Settings->Interval = settings->Interval; + } + + if ((settings->Values & ReadonlySet) == ReadonlySet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueLong, SettingKeys->Readonly, settings->Readonly); + } + else if (Globals->Settings->Readonly != settings->Readonly) + { + LogInfo(settingChangedLong, SettingKeys->Readonly, Globals->Settings->Readonly, + settings->Readonly); + } + else if (!quiet) + { + LogTrace(settingSetLong, SettingKeys->Readonly, settings->Readonly); + } + Globals->Settings->Readonly = settings->Readonly; + } + + if ((settings->Values & DestinationAddressSet) == DestinationAddressSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueString, SettingKeys->DestinationAddress, + settings->DestinationAddress); + } + else if (Stricmp((STRPTR)Globals->Settings->DestinationAddress, + (STRPTR)settings->DestinationAddress) != 0) + { + LogInfo(settingChangedString, SettingKeys->DestinationAddress, + Globals->Settings->DestinationAddress, settings->DestinationAddress); + } + else if (!quiet) + { + LogTrace(settingSetString, SettingKeys->DestinationAddress, settings->DestinationAddress); + } + + if (Globals->Settings->DestinationAddress != NULL) + { + FreeMemSafe(Globals->Settings->DestinationAddress); + } + Globals->Settings->DestinationAddress = StrDupSafe(settings->DestinationAddress); + } + + if ((settings->Values & DestinationPortSet) == DestinationPortSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueString, SettingKeys->DestinationPort, + settings->DestinationPort); + } + else if (Stricmp((STRPTR)Globals->Settings->DestinationPort, + (STRPTR)settings->DestinationPort) != 0) + { + LogInfo(settingChangedString, SettingKeys->DestinationPort, + Globals->Settings->DestinationPort, settings->DestinationPort); + } + else if (!quiet) + { + LogTrace(settingSetString, SettingKeys->DestinationPort, settings->DestinationPort); + } + + if (Globals->Settings->DestinationPort != NULL) + { + FreeMemSafe(Globals->Settings->DestinationPort); + } + Globals->Settings->DestinationPort = StrDupSafe(settings->DestinationPort); + } + + if ((settings->Values & ThresholdSet) == ThresholdSet) + { + char before[MAXLONGLONGCHARSIZE]; + char after[MAXLONGLONGCHARSIZE]; + + if (settings->Type == DefaultSettingType) + { + LongLongToStr(settings->Threshold, after); + LogTrace(settingValueString, SettingKeys->Threshold, after); + } + else if (Globals->Settings->Threshold != settings->Threshold) + { + LongLongToStr(Globals->Settings->Threshold, before); + LongLongToStr(settings->Threshold, after); + LogInfo(settingChangedString, SettingKeys->Threshold, before, after); + } + else if (!quiet) + { + LongLongToStr(Globals->Settings->Threshold, before); + LogTrace(settingSetString, SettingKeys->Threshold, before); + } + Globals->Settings->Threshold = settings->Threshold; + } + + if ((settings->Values & LogFileSet) == LogFileSet) + { + if (settings->Type == DefaultSettingType) + { + LogTrace(settingValueString, SettingKeys->LogFile, + settings->LogFile != NULL ? settings->LogFile : noLogFile); + } + else if (!(Globals->Settings->LogFile == NULL && settings->LogFile == NULL) || + StrnCmp(Globals->Locale, + (STRPTR)Globals->Settings->LogFile, + (STRPTR)settings->LogFile, + (LONG)MAXSETTINGLINELEN, SC_ASCII) != 0) + { + LogInfo(settingChangedString, SettingKeys->LogFile, + Globals->Settings->LogFile != NULL ? Globals->Settings->LogFile : noLogFile, + settings->LogFile != NULL ? settings->LogFile : noLogFile); + } + else if (!quiet) + { + LogTrace(settingSetString, SettingKeys->LogFile, + settings->LogFile != NULL ? settings->LogFile : noLogFile); + } + + if (Globals->Settings->LogFile != NULL) + { + FreeMemSafe(Globals->Settings->LogFile); + } + Globals->Settings->LogFile = settings->LogFile != NULL + ? StrDupSafe(settings->LogFile) + : NULL; + } +} + +void CacheSettings(struct AppSettings *settings) +{ + if (cachedSettings != NULL) + { + FreeSettings(cachedSettings); + } + cachedSettings = settings; +} + +void ApplySettings() +{ + ApplyAppSettings((struct AppSettings *)&DefaultSettings, false); + + if (fileSettings != NULL) + { + ApplyAppSettings(fileSettings, false); + FreeSettings(fileSettings); + } + + if (cachedSettings != NULL) + { + ApplyAppSettings(cachedSettings, false); + FreeSettings(cachedSettings); + } +} + +static void ValidateInterval(void) +{ + if (Globals->Settings->Interval < INTERVAL_MIN) + { + LogInfo(settingTooLow, + SettingKeys->Interval, + INTERVAL_MIN); + LogInfo(settingChangedLong, + SettingKeys->Interval, + Globals->Settings->Interval, + INTERVAL_MIN); + Globals->Settings->Interval = INTERVAL_MIN; + } +} + +static void ValidateTimeout(void) +{ + if (Globals->Settings->Timeout < TIMEOUT_MIN) + { + LogInfo(settingTooLow, + SettingKeys->Timeout, + TIMEOUT_MIN); + LogInfo(settingChangedLong, + SettingKeys->Timeout, + Globals->Settings->Timeout, + TIMEOUT_MIN); + Globals->Settings->Timeout = TIMEOUT_MIN; + } + + if (Globals->Settings->Timeout > Globals->Settings->Interval) + { + LogInfo(settingGreaterThan, + SettingKeys->Timeout, + SettingKeys->Interval); + LogInfo(settingChangedLong, + SettingKeys->Timeout, + Globals->Settings->Timeout, + Globals->Settings->Interval); + Globals->Settings->Timeout = Globals->Settings->Interval; + } + + if (Globals->Settings->Timeout > TIMEOUT_MAX) + { + LogInfo(settingTooHigh, + SettingKeys->Timeout, + TIMEOUT_MAX); + LogInfo(settingChangedLong, + SettingKeys->Timeout, + Globals->Settings->Timeout, + TIMEOUT_MAX); + Globals->Settings->Timeout = TIMEOUT_MAX; + } +} + +static void ValidatePriority(void) +{ + if (Globals->Settings->Priority < PRIORITY_MIN) + { + LogInfo(settingTooLow, + SettingKeys->Priority, + PRIORITY_MIN); + LogInfo(settingChangedLong, + SettingKeys->Priority, + Globals->Settings->Priority, + PRIORITY_MIN); + Globals->Settings->Priority = PRIORITY_MIN; + } + + if (Globals->Settings->Priority > PRIORITY_MAX) + { + LogInfo(settingTooHigh, + SettingKeys->Priority, + PRIORITY_MAX); + LogInfo(settingChangedLong, + SettingKeys->Priority, + Globals->Settings->Priority, + PRIORITY_MAX); + Globals->Settings->Priority = PRIORITY_MAX; + } +} + +static void ValidateThreshold(void) +{ + if (Globals->Settings->Threshold < THRESHOLD_MIN) + { + LogInfo(settingTooLow, + SettingKeys->Threshold, + THRESHOLD_MIN); + LogInfo(settingChangedLong, + SettingKeys->Threshold, + Globals->Settings->Threshold, + THRESHOLD_MIN); + Globals->Settings->Threshold = THRESHOLD_MIN; + } +} + +static void ValidateVerbose(void) +{ + if (Globals->Settings->Verbose < VERBOSE_MIN) + { + LogInfo(settingTooLow, + SettingKeys->Verbose, + VERBOSE_MIN); + LogInfo(settingChangedLong, + SettingKeys->Verbose, + Globals->Settings->Verbose, + VERBOSE_MIN); + Globals->Settings->Verbose = VERBOSE_MIN; + } + + if (Globals->Settings->Verbose > VERBOSE_MAX) + { + LogInfo(settingTooHigh, + SettingKeys->Verbose, + VERBOSE_MAX); + LogInfo(settingChangedLong, + SettingKeys->Verbose, + Globals->Settings->Verbose, + VERBOSE_MAX); + Globals->Settings->Verbose = VERBOSE_MAX; + } +} + +void SanitizeSettings(void) +{ + ValidateInterval(); + ValidateTimeout(); + ValidatePriority(); + ValidateThreshold(); + ValidateVerbose(); +} diff --git a/state.h b/state.h new file mode 100644 index 0000000..edba8fd --- /dev/null +++ b/state.h @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef STATE_H_INCLUDED +#define STATE_H_INCLUDED + +#include "config.h" + +#define PrioritySet 0x001 +#define VerboseSet 0x002 +#define TimeoutSet 0x004 +#define IntervalSet 0x008 +#define ReadonlySet 0x010 +#define DestinationAddressSet 0x020 +#define DestinationPortSet 0x040 +#define ThresholdSet 0x080 +#define LogFileSet 0x200 + +#define DefaultSettingType 0x01 +#define FileSettingType 0x02 +#define CliSettingType 0x03 +#define WbSettingType 0x04 +#define GlobalSettingType 0x10 + +struct AppBroker +{ + CxObj *Object; + short ShutdownSigBit; + struct Task *Task; + struct MsgPort *BrokerPort; + struct MsgPort *UserPort; + struct MsgPort *ReplyPort; +}; + +struct AppCom; +struct AppSettingWindowGadgets; + +struct AppSettingWindow +{ + struct Process *Task; + struct Screen *Screen; + struct Window *SettingWindow; + struct Gadget *GadgetList; + struct MsgPort *UserPort; + void *VisualInfo; + char *PriorityText; + char *ThresholdText; + char *TimeZoneText; + long Width; + long Height; + struct AppSettings *Settings; + struct AppSettingWindowGadgets *Gadgets; +}; + +struct AppSettings +{ + long Priority; + long Verbose; + long Timeout; + long Interval; + long Readonly; + long long Threshold; + char *DestinationAddress; + char *DestinationPort; + char *LogFile; + long Values; + long Type; +}; + +struct AppState +{ + bool Active; + bool ShuttingDown; + struct timeval LastNtpSync; + struct Locale *Locale; + struct AppCom *Syncer; + struct AppBroker *Broker; + struct AppSettingWindow *Window; + struct AppSignals *Signals; + struct AppSettings *Settings; +}; + +struct AppSettingKeys +{ + const char *Priority; + const char *Verbose; + const char *Timeout; + const char *Interval; + const char *Readonly; + const char *DestinationAddress; + const char *DestinationPort; + const char *Threshold; + const char *LogFile; +}; + +typedef void (*ParseSettingFunction)(struct AppSettings *, void *); + +struct SettingFunc +{ + const char *Name; + ParseSettingFunction Function; +}; + +extern struct AppState *Globals; +extern struct AppSettingKeys *SettingKeys; +extern const struct SettingFunc settingFunctions[]; +extern const struct AppSettings DefaultSettings; + +void InitState(void); +void DestroyState(void); +void SanitizeSettings(void); +void ShowSettings(void); +void LoadSettings(void); +void SaveSettings(bool); + +struct AppSettings *CreateSettings(long); +struct AppSettings *CopySettings(const struct AppSettings *); +void ApplyAppSettings(struct AppSettings *, bool); +void FreeSettings(struct AppSettings *); +void CacheSettings(struct AppSettings *); +void ApplySettings(void); + +#endif diff --git a/string.c b/string.c new file mode 100644 index 0000000..22f795e --- /dev/null +++ b/string.c @@ -0,0 +1,391 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +static const char *alphaNumerics = "0123456789"; +static const int base = 10; + +struct RawDoFmtStream +{ + char *buf; + int size; +}; + +/* + * Get the length of a null terminated string. + */ +int StrLen(const char *string) +{ + char *i = (char *)string; + char *s = i; + while (*i) + i++; + return (int)(i - s); +} + +/* + * Copies the string pointed to by src to dest. + */ +char *StrCopy(char *dest, const char *src) +{ + char *q = (char *)src; + char *p = dest; + + while ((*p++ = *q++)) + ; + + *p = '\0'; + return dest; +} + +char *AppendText(char *start, const char *text) +{ + char *p = start; + char *q = (char *)text; + + while (*q != '\0') + *p++ = *q++; + + *p = '\0'; + return p; +} + +int TrimRight(char *string) +{ + char *i = string; + char *s = i; + + while (*i) + i++; + + if (i == s) + return 0; + + i--; + while (*i == ' ' || *i == '\r' || *i == '\n' || *i == '\t') + *i-- = '\0'; + + return (int)(i - s + 1); +} + +/* + * Find the first occurrence of a character. + */ +char *StrChr(const char *start, const char c, int len) +{ + char *i = (char *)start; + int j = 0; + + if (i == NULL) + return NULL; + + do + { + if (*i == c) + return i; + + i++; + j++; + } while (j < len); + + return NULL; +} + +/* + * Find the last occurrence of a character. + */ +char *StrRChr(const char *start, const char c, int len) +{ + char *i = (char *)start; + char *l = NULL; + int j = 0; + + if (i == NULL) + return NULL; + + do + { + if (*i == c) + l = i; + + i++; + j++; + } while (j < len); + + return l; +} + +/* + * Convert a long to a null terminated string. + */ +int LongToStr(signed long value, char *string) +{ + unsigned int len, count = 0; + unsigned long current = value; + char chars[12]; + char *p = chars; + char *q = string; + bool negative = false; + + if (value < 0) + { + current = -value; + negative = true; + } + + do + { + unsigned long remainder = current % base; + *p++ = alphaNumerics[remainder]; + current /= base; + count++; + } while (current >= 1); + + p--; + len = count; + + if (negative) + { + *q++ = '-'; + len++; + } + + while (count-- != 0) + *q++ = *p--; + + *q = '\0'; + return len; +} + +/* + * Convert a long long to a null terminated string. + */ +int LongLongToStr(signed long long value, char *string) +{ + unsigned int len, count = 0; + unsigned long long current = value; + char chars[MAXLONGLONGCHARSIZE]; + char *p = chars; + char *q = string; + bool negative = false; + + if (value < 0) + { + current = -value; + negative = true; + } + + do + { + unsigned long long remainder = current % base; + *p++ = alphaNumerics[remainder]; + current /= base; + count++; + } while (current >= 1); + + p--; + len = count; + + if (negative) + { + *q++ = '-'; + len++; + } + + while (count-- != 0) + *q++ = *p--; + + *q = '\0'; + return len; +} + +/* + * Convert a null terminated string to a long long. + */ +int StrToLongLong(char *string, unsigned long long *value) +{ + signed long long result = 0; + bool negative = false; + char *p = string; + int count = 0; + + if (*p == '-') + { + negative = true; + p++; + } + + while (*p != '\0') + { + if (*p < '0' || *p > '9' || count > 20) + { + return -1; + } + + result *= 10; + result += (*p++) - '0'; + } + + *value = negative ? -result : result; + return count; +} + +/* + * Try to parse a null terminated string to a long. + */ +bool TryParseLong(char *string, long *value) +{ + LONG characters, parsedValue; + characters = StrToLong((STRPTR)string, &parsedValue); + if (characters != -1) + { + *value = parsedValue; + return true; + } + + *value = 0; + return false; +} + +/* + * Try to parse a null terminated string to a long. + */ +bool TryParseLongLong(char *string, long long *value) +{ + unsigned long long parsedValue; + long characters; + characters = StrToLongLong(string, &parsedValue); + if (characters != -1) + { + *value = parsedValue; + return true; + } + + *value = 0; + return false; +} + +#ifdef AOS3 + +static void RawDoFmtChar(REG(d0, UBYTE c), REG(a3, struct RawDoFmtStream *s)) +{ + if (s->size > 0) + { + *(s->buf)++ = c; + (s->size)--; + + if (s->size == 1) + { + *(s->buf) = '\0'; + s->size = 0; + } + } +} + +/* + * Formats and stores a series of characters and values. + */ +int VSNPrintf(char *str, size_t size, const char *format, va_list ap) +{ + int result = 0; + + if (str != NULL) + { + struct RawDoFmtStream s; + + s.buf = str; + s.size = size; + + RawDoFmt(format, (void *)ap, (void (*)())RawDoFmtChar, (void *)&s); + + if ((result = (size - s.size)) != size) + --result; + } + + return result; +} + +/* + * Formats and stores a series of characters and values in the array buffer. + */ +int SNPrintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = VSNPrintf(str, size, format, args); + va_end(args); + + return result; +} + +#endif + +/* + * Split filename in path and filename. + */ +void SplitFileName(const char *s, char *path, char *name) +{ + int len, x, y; + char *c; + + if (s == NULL || *s == '\0') + { + *path = '\0'; + *name = '\0'; + return; + } + + len = StrLen(s); + c = StrRChr(s, '/', len); + if (c == NULL) + { + c = StrChr(s, ':', len); + if (c == NULL) + { + *path = '\0'; + CopyMem((char *)s, name, len); + return; + } + else + { + // Include : + x = (c - s + 1); + } + } + else + { + x = (c - s); + } + + y = len - (c - s); + + *(char *)(path + x) = '\0'; + CopyMem((char *)s, path, x); + CopyMem(++c, name, y); +} \ No newline at end of file diff --git a/string.h b/string.h new file mode 100644 index 0000000..4feca53 --- /dev/null +++ b/string.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef STRING_H_INCLUDED +#define STRING_H_INCLUDED + +#include "compiler.h" +#include +#include + +int StrLen(const char *); +char *StrCopy(char *, const char *); +char *AppendText(char *, const char *); +int TrimRight(char *); +char *StrChr(const char *, const char, int); +char *StrRChr(const char *, const char, int); +int LongToStr(signed long, char *); +int LongLongToStr(signed long long, char *); +int StrToLongLong(char *, unsigned long long *); +bool TryParseLong(char *, long *); +bool TryParseLongLong(char *, long long *); +int VSNPrintf(char *, size_t, const char *, va_list); +int SNPrintf(char *, size_t, const char *, ...); +void SplitFileName(const char *, char *, char *); + +#endif diff --git a/time.c b/time.c new file mode 100644 index 0000000..bba0e21 --- /dev/null +++ b/time.c @@ -0,0 +1,235 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "time.h" +#include "mem.h" + +#include + +/* + * 2922 is the number of days between 1.1.1970 and 1.1.1978 + * (2 leap years and 6 normal) + * 2922 * 24 * 60 * 60 = 252460800 + */ +#define AMIGA_OFFSET 252460800 +#define TIMER_ERROR 10 +#define TIMER_OK 0 + +struct TimerInfo +{ + bool TimerTrigged; + struct MsgPort *TimerPort; + struct timerequest *TimerIO; +}; + +struct Device *TimerBase = NULL; +static struct timerequest *TimerIO = NULL; +static long utcOffset = 0; + +/* + * Set up pointer for timer functions. + */ +struct Device *OpenTimerBase(void) +{ + LONG error; + + TimerIO = (struct timerequest *)AllocMemSafe(sizeof(struct timerequest)); + if (TimerIO == NULL) + { + return NULL; + } + + error = OpenDevice((STRPTR)TIMERNAME, UNIT_MICROHZ, (struct IORequest *)TimerIO, 0); + if (error != 0) + { + FreeMemSafe(TimerIO); + TimerIO = NULL; + return NULL; + } + + TimerBase = TimerIO->tr_node.io_Device; + return TimerBase; +} + +void CloseTimerBase(void) +{ + if (TimerBase != NULL && TimerIO != NULL) + { + CloseDevice((struct IORequest *)TimerIO); + } + + if (TimerIO != NULL) + { + FreeMemSafe(TimerIO); + } +} + +/* + * Open a timer device with UNIT_VBLANK. + */ +struct TimerInfo *CreateTimer(bool setTimerBase) +{ + LONG error; + static const char *name = APP_SHORT_NAME " Timer Message Port"; + struct TimerInfo *info = (struct TimerInfo *)AllocMemSafe(sizeof(struct TimerInfo)); + if (info == NULL) + return NULL; + + info->TimerPort = CreateMsgPort(); + if (info->TimerPort == NULL) + { + DeleteTimer(info); + return NULL; + } + + info->TimerPort->mp_Node.ln_Name = (char *)name; + info->TimerPort->mp_Node.ln_Pri = 0; + + info->TimerIO = (struct timerequest *)CreateExtIO(info->TimerPort, sizeof(struct timerequest)); + if (info->TimerIO == NULL) + { + DeleteTimer(info); + return NULL; + } + + error = OpenDevice( + (STRPTR)TIMERNAME, UNIT_MICROHZ, + (struct IORequest *)info->TimerIO, 0); + + if (error != 0) + { + DeleteTimer(info); + return NULL; + } + + return info; +} + +void SetTimer(struct TimerInfo *info) +{ + info->TimerIO->tr_node.io_Command = TR_ADDREQUEST; + info->TimerIO->tr_time.tv_secs = Globals->Settings->Interval / 1000; + info->TimerIO->tr_time.tv_micro = Globals->Settings->Interval % 1000; + SendIO((struct IORequest *)info->TimerIO); + info->TimerTrigged = true; +} + +ULONG GetTimerSigMask(struct TimerInfo *info) +{ + return (1 << info->TimerPort->mp_SigBit); +} + +void DeleteTimer(struct TimerInfo *info) +{ + if (info->TimerIO != NULL) + { + AbortIO((struct IORequest *)info->TimerIO); + if (info->TimerTrigged) + { + WaitIO((struct IORequest *)info->TimerIO); + } + CloseDevice((struct IORequest *)info->TimerIO); + DeleteExtIO((struct IORequest *)info->TimerIO); + info->TimerIO = NULL; + } + + if (info->TimerPort != NULL) + { + DeleteMsgPort(info->TimerPort); + } + + FreeMemSafe(info); +} + +void InitUtcOffset(void) +{ + char timeZone[10]; + int gmtoffset = Globals->Locale->loc_GMTOffset * 60; + utcOffset = gmtoffset + AMIGA_OFFSET; + GetTimeZoneText(timeZone); + LogWarning("Local time zone is %s", timeZone); +} + +void GetTimeZoneText(char *text) +{ + SNPrintf(text, 10, "UTC%s%ld:%02ld", + -Globals->Locale->loc_GMTOffset > 0 ? "+" : "", + (long)-Globals->Locale->loc_GMTOffset / 60, + (long)-Globals->Locale->loc_GMTOffset % 60); +} + +void Unix2Amiga(struct timeval *unix, struct timeval *tv) +{ + tv->tv_secs = unix->tv_secs + AMIGA_OFFSET; + tv->tv_micro = unix->tv_micro; +} + +void Utc2Local(struct timeval *utc, struct timeval *tv) +{ + int gmtoffset = Globals->Locale->loc_GMTOffset * 60; + tv->tv_secs = utc->tv_secs - gmtoffset; + tv->tv_micro = utc->tv_micro; +} + +void GetTimeOfDay(struct timeval *tv) +{ + GetSysTime(tv); + tv->tv_secs = tv->tv_secs + utcOffset; +} + +void GetLocalTimeOfDay(struct timeval *tv) +{ + GetSysTime(tv); +} + +void SetTimeOfDay(const struct TimerInfo *info, const struct timeval *tv) +{ + info->TimerIO->tr_node.io_Command = TR_SETSYSTIME; + info->TimerIO->tr_time.tv_secs = (long)tv->tv_secs - utcOffset; + info->TimerIO->tr_time.tv_micro = tv->tv_micro; + DoIO((struct IORequest *)info->TimerIO); +} + +void SaveTimeOfDay(const struct timeval *tv) +{ + WriteBattClock((long)tv->tv_secs - utcOffset); +} + +void SystemTimeString(char *time, char *date, char *day) +{ + struct DateTime dt; + + DateStamp(&dt.dat_Stamp); + + dt.dat_Format = FORMAT_DOS; + dt.dat_Flags = 0; + dt.dat_StrDay = (void *)day; + dt.dat_StrDate = (void *)date; + dt.dat_StrTime = (void *)time; + + DateToStr(&dt); +} diff --git a/time.h b/time.h new file mode 100644 index 0000000..e4eced5 --- /dev/null +++ b/time.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TIME_H_INCLUDED +#define TIME_H_INCLUDED + +#include "config.h" + +struct TimerInfo; + +struct Device *OpenTimerBase(void); +void CloseTimerBase(void); +struct TimerInfo *CreateTimer(bool); +void SetTimer(struct TimerInfo *); +void DeleteTimer(struct TimerInfo *); +ULONG GetTimerSigMask(struct TimerInfo *info); + +void InitUtcOffset(void); +void GetTimeZoneText(char *text); +void GetTimeOfDay(struct timeval *tv); +void SetTimeOfDay(const struct TimerInfo *, const struct timeval *tv); +void SaveTimeOfDay(const struct timeval *tv); +void GetLocalTimeOfDay(struct timeval *tv); +void Unix2Amiga(struct timeval *unix, struct timeval *tv); +void Utc2Local(struct timeval *utc, struct timeval *tv); +void SystemTimeString(char *time, char *date, char *day); + +#endif diff --git a/win.h b/win.h new file mode 100644 index 0000000..725c966 --- /dev/null +++ b/win.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WIN_H_INCLUDED +#define WIN_H_INCLUDED + +#include +#include +#include +#include + +#define GID_SERVER 1001 +#define GID_PORT 1002 +#define GID_TIMEOUT 1003 +#define GID_INTERVAL 1004 +#define GID_THRESHOLD 1005 +#define GID_VERBOSE 1006 +#define GID_READONLY 1007 +#define GID_PRIORITY 1008 +#define GID_SAVE 1009 +#define GID_USE 1010 +#define GID_CANCEL 1011 + +/* win_main.c */ +void ShowSettingWindow(void); +void HideSettingWindow(void); +bool CheckSettingWindowClosed(void); + +/* win_gad.c */ +bool CreateGadgets(void); +void UseSettings(void); +void SetServer(void); +void SetPort(void); +void SetTimeout(void); +void SetInterval(void); +void SetThreshold(void); +void SetVerbose(void); +void SetCxPriority(void); +void ShowLastSync(void); +void SetDefaultSettings(void); + +#endif diff --git a/win_gad.c b/win_gad.c new file mode 100644 index 0000000..329c60a --- /dev/null +++ b/win_gad.c @@ -0,0 +1,686 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "message.h" +#include "state.h" +#include "time.h" +#include "mem.h" +#include "win.h" + +struct AppSettingWindowGadgets +{ + struct Gadget *ServerGadget; + struct Gadget *PortGadget; + struct Gadget *TimeoutGadget; + struct Gadget *IntervalGadget; + struct Gadget *ThresholdGadget; + struct Gadget *VerboseGadget; + struct Gadget *PriorityGadget; + struct Gadget *TimeZoneGadget; + struct Gadget *LastSyncGadget; +}; + +static const char *serverLabel = "Server address"; +static const char *portLabel = "Server port"; +static const char *timeoutLabel = "Timeout (ms)"; +static const char *intervalLabel = "Interval (ms)"; +static const char *thresholdLabel = "Threshold (us)"; +static const char *verboseLabel = "Verbose level"; +static const char *priorityLabel = "CX priority"; +static const char *timeZoneLabel = "Time zone"; +static const char *lastSyncLabel = "Last sync"; +static const char *saveLabel = "Save"; +static const char *useLabel = "Use"; +static const char *cancelLabel = "Cancel"; + +static char *verboseLevel[] = { + "Show none (0)", + "Show some (1)", + "Show more (2)", + "Show all (3)", + NULL}; + +bool CreateGadgets(void) +{ + struct Gadget *gadget; + struct NewGadget *ng; + long x, y, w, h; + long textWidth, boxWidth; + long col1, col2; + long labelId; + long tmp1, tmp2; + + Globals->Window->Gadgets = (struct AppSettingWindowGadgets *) + AllocMemSafe(sizeof(struct AppSettingWindowGadgets)); + + ng = (struct NewGadget *)AllocMemSafe(sizeof(struct NewGadget)); + if (!ng) + return false; + + gadget = CreateContext(&Globals->Window->GadgetList); + if (!gadget) + return false; + + ng->ng_VisualInfo = Globals->Window->VisualInfo; + ng->ng_TextAttr = Globals->Window->Screen->Font; + ng->ng_Flags = 0; + + w = Globals->Window->Screen->RastPort.TxWidth; + h = Globals->Window->Screen->RastPort.TxHeight; + + x = Globals->Window->Screen->WBorLeft + 8; + y = Globals->Window->Screen->WBorTop + h + 9; + + col1 = x; + textWidth = 13 + VARSIZE; + + col2 = col1 + w * textWidth; + boxWidth = 13 + VARSIZE * 2; + + ng->ng_TopEdge = y; + ng->ng_Height = h + 6; + + labelId = 5000; + + // Server + ng->ng_LeftEdge = col1; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)serverLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_SERVER; + gadget = CreateGadget( + STRING_KIND, gadget, ng, + GTST_String, (IPTR)Globals->Settings->DestinationAddress, + TAG_END); + if (!gadget) + return false; + + Globals->Window->Gadgets->ServerGadget = gadget; + + // Port + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)portLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_PORT; + gadget = CreateGadget( + STRING_KIND, gadget, ng, + GTST_String, (IPTR)Globals->Settings->DestinationPort, + TAG_END); + if (!gadget) + return false; + + Globals->Window->Gadgets->PortGadget = gadget; + + // Interval + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTNM_MaxNumberLen, 8, + GTTX_Text, (IPTR)intervalLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_INTERVAL; + gadget = CreateGadget( + INTEGER_KIND, gadget, ng, + GTNM_MaxNumberLen, 8, + GTIN_Number, Globals->Settings->Interval, + TAG_END); + if (!gadget) + return false; + + gadget->Flags |= GFLG_TABCYCLE; + Globals->Window->Gadgets->IntervalGadget = gadget; + + // Timeout + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)timeoutLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_TIMEOUT; + gadget = CreateGadget( + INTEGER_KIND, gadget, ng, + GTNM_MaxNumberLen, 8, + GTIN_Number, Globals->Settings->Timeout, + TAG_END); + if (!gadget) + return false; + + gadget->Flags |= GFLG_TABCYCLE; + Globals->Window->Gadgets->TimeoutGadget = gadget; + + // Threshold + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)thresholdLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_THRESHOLD; + gadget = CreateGadget( + STRING_KIND, gadget, ng, + GTST_String, (IPTR)Globals->Window->ThresholdText, + TAG_END); + if (!gadget) + return false; + + gadget->Flags |= GFLG_TABCYCLE; + Globals->Window->Gadgets->ThresholdGadget = gadget; + + // Verbose + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)verboseLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = GID_VERBOSE; + gadget = CreateGadget( + CYCLE_KIND, gadget, ng, + GTCY_Labels, (IPTR)verboseLevel, + GTCY_Active, Globals->Settings->Verbose, + TAG_END); + if (!gadget) + return false; + + gadget->Flags |= GFLG_TABCYCLE; + Globals->Window->Gadgets->VerboseGadget = gadget; + + // Priority + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)priorityLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = (boxWidth * w + 20) / 2; + ng->ng_GadgetID = GID_PRIORITY; + + gadget = CreateGadget( + STRING_KIND, gadget, ng, + GTST_String, (IPTR)Globals->Window->PriorityText, + TAG_END); + if (!gadget) + return false; + + gadget->Flags |= GFLG_TABCYCLE; + Globals->Window->Gadgets->PriorityGadget = gadget; + + // Time zone + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)timeZoneLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)Globals->Window->TimeZoneText, + TAG_END); + if (!gadget) + return false; + + Globals->Window->Gadgets->TimeZoneGadget = gadget; + + // Last sync + ng->ng_LeftEdge = col1; + ng->ng_TopEdge += ng->ng_Height + 4; + ng->ng_Width = textWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + GTTX_Text, (IPTR)lastSyncLabel, + TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge = col2; + ng->ng_Width = boxWidth * w + 20; + ng->ng_GadgetID = labelId++; + gadget = CreateGadget( + TEXT_KIND, gadget, ng, + TAG_END); + if (!gadget) + return false; + + Globals->Window->Gadgets->LastSyncGadget = gadget; + + tmp1 = ng->ng_LeftEdge + ng->ng_Width - 18; // Why is this 18? + tmp2 = tmp1 / 3; + + ng->ng_TopEdge += ng->ng_Height + 6; + ng->ng_Width = tmp2; + ng->ng_LeftEdge = col1; + ng->ng_GadgetText = (STRPTR)saveLabel; + ng->ng_GadgetID = GID_SAVE; + ng->ng_Flags = 0; + gadget = CreateGadget(BUTTON_KIND, gadget, ng, TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge += ng->ng_Width + 4; + ng->ng_GadgetText = (STRPTR)useLabel; + ng->ng_GadgetID = GID_USE; + gadget = CreateGadget(BUTTON_KIND, gadget, ng, TAG_END); + if (!gadget) + return false; + + ng->ng_LeftEdge += ng->ng_Width + 4; + ng->ng_GadgetText = (STRPTR)cancelLabel; + ng->ng_GadgetID = GID_CANCEL; + gadget = CreateGadget(BUTTON_KIND, gadget, ng, TAG_END); + if (!gadget) + return false; + + Globals->Window->Height = ng->ng_TopEdge + ng->ng_Height; + Globals->Window->Width = textWidth * w + boxWidth * w; + + FreeMemSafe(ng); + return true; +} + +static char *BuildLogText(const char *setting, char *oldValue, char *newValue) +{ + static const char *txt1 = " changed: "; + static const char *txt2 = " -> "; + char *p = AllocMemSafe(SETTINGMESSAGELEN); + char *s = p; + p = AppendText(p, (char *)setting); + p = AppendText(p, (char *)txt1); + p = AppendText(p, oldValue); + p = AppendText(p, (char *)txt2); + p = AppendText(p, newValue); + return s; +} + +void UseSettings(void) +{ + SetServer(); + SetPort(); + SetInterval(); + SetTimeout(); + SetThreshold(); + SetVerbose(); + SetCxPriority(); +} + +void SetServer(void) +{ + char *server, *text; + + GT_GetGadgetAttrs( + Globals->Window->Gadgets->ServerGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)&server, + TAG_END); + + if (Stricmp((STRPTR)server, (STRPTR)Globals->Settings->DestinationAddress) == 0) + return; + + text = BuildLogText(SettingKeys->DestinationAddress, + Globals->Settings->DestinationAddress, + server); + FreeMemSafe(Globals->Settings->DestinationAddress); + Globals->Settings->DestinationAddress = StrDupSafe(server); + SendInfoMessage(text); + SendMessage(ATK_RESTART); +} + +void SetPort(void) +{ + char *port, *text; + + GT_GetGadgetAttrs( + Globals->Window->Gadgets->PortGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)&port, + TAG_END); + + if (Stricmp((STRPTR)port, (STRPTR)Globals->Settings->DestinationPort) == 0) + return; + + text = BuildLogText(SettingKeys->DestinationPort, + Globals->Settings->DestinationPort, + port); + FreeMemSafe(Globals->Settings->DestinationPort); + Globals->Settings->DestinationPort = StrDupSafe(port); + SendInfoMessage(text); + SendMessage(ATK_RESTART); +} + +void SetInterval(void) +{ + unsigned long value; + + GT_GetGadgetAttrs( + Globals->Window->Gadgets->IntervalGadget, + Globals->Window->SettingWindow, NULL, + GTIN_Number, (IPTR)&value, + TAG_END); + + if (value < INTERVAL_MIN) + value = INTERVAL_MIN; + + if (Globals->Settings->Interval != value) + { + char *text; + char newValue[MAXLONGCHARSIZE]; + char oldValue[MAXLONGCHARSIZE]; + + LongToStr(Globals->Settings->Interval, oldValue); + LongToStr(value, newValue); + Globals->Settings->Interval = value; + text = BuildLogText(SettingKeys->Interval, oldValue, newValue); + SendInfoMessage(text); + } +} + +void SetTimeout(void) +{ + unsigned long value; + GT_GetGadgetAttrs( + Globals->Window->Gadgets->TimeoutGadget, + Globals->Window->SettingWindow, NULL, + GTIN_Number, (IPTR)&value, + TAG_END); + + if (value < TIMEOUT_MIN) + value = TIMEOUT_MIN; + else if (value > TIMEOUT_MAX) + value = TIMEOUT_MAX; + + if (Globals->Settings->Timeout != value) + { + char *text; + char newValue[MAXLONGCHARSIZE]; + char oldValue[MAXLONGCHARSIZE]; + + LongToStr(Globals->Settings->Timeout, oldValue); + LongToStr(value, newValue); + Globals->Settings->Timeout = value; + text = BuildLogText(SettingKeys->Timeout, oldValue, newValue); + SendInfoMessage(text); + } +} + +void SetThreshold(void) +{ + unsigned long long value; + char *tempString; + int result; + + GT_GetGadgetAttrs( + Globals->Window->Gadgets->ThresholdGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)&tempString, + TAG_END); + + result = StrToLongLong(tempString, &value); + if (result != -1 && Globals->Settings->Threshold != value) + { + char *text; + char newValue[MAXLONGLONGCHARSIZE]; + char oldValue[MAXLONGLONGCHARSIZE]; + + LongLongToStr(Globals->Settings->Threshold, oldValue); + LongLongToStr(value, newValue); + Globals->Settings->Threshold = value; + text = BuildLogText(SettingKeys->Threshold, oldValue, newValue); + SendInfoMessage(text); + SendMessage(ATK_RESTART); + } +} + +void SetVerbose(void) +{ + long value; + GT_GetGadgetAttrs( + Globals->Window->Gadgets->VerboseGadget, + Globals->Window->SettingWindow, NULL, + GTCY_Active, (IPTR)&value, + TAG_END); + + if (value < VERBOSE_MIN) + value = VERBOSE_MIN; + else if (value > VERBOSE_MAX) + value = VERBOSE_MAX; + + if (Globals->Settings->Verbose != value) + { + char *text; + char newValue[MAXLONGCHARSIZE]; + char oldValue[MAXLONGCHARSIZE]; + + LongToStr(Globals->Settings->Verbose, oldValue); + LongToStr(value, newValue); + text = BuildLogText(SettingKeys->Verbose, oldValue, newValue); + SendInfoMessage(text); + Globals->Settings->Verbose = value; + } +} + +void SetCxPriority(void) +{ + LONG value; + char *tempString; + int result; + + GT_GetGadgetAttrs( + Globals->Window->Gadgets->PriorityGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)&tempString, + TAG_END); + + result = StrToLong((STRPTR)tempString, &value); + if (result != -1) + { + if (value < PRIORITY_MIN) + value = PRIORITY_MIN; + else if (value > PRIORITY_MAX) + value = PRIORITY_MAX; + + if (Globals->Settings->Priority != value) + { + char *text; + char newValue[MAXLONGCHARSIZE]; + char oldValue[MAXLONGCHARSIZE]; + + LongToStr(Globals->Settings->Priority, oldValue); + LongToStr(value, newValue); + Globals->Settings->Priority = value; + text = BuildLogText(SettingKeys->Priority, oldValue, newValue); + SetBrokerPriority(value); + SendInfoMessage(text); + } + } +} + +void ShowLastSync(void) +{ + static char label[10]; + struct timeval nowLocal, last, lastLocal; + struct ClockData clockdata; + struct DateTime dateTime = { + .dat_Stamp = {.ds_Days = 0}, + .dat_Format = FORMAT_DOS, + .dat_Flags = 0, + .dat_StrDay = NULL, + .dat_StrDate = NULL, + .dat_StrTime = (STRPTR)label}; + long seconds; + + if (Globals->LastNtpSync.tv_secs == 0) + { + static const char *never = "Not synced yet"; + GT_SetGadgetAttrs( + Globals->Window->Gadgets->LastSyncGadget, + Globals->Window->SettingWindow, NULL, + GTTX_Text, (IPTR)never, + TAG_END); + + return; + } + + GetLocalTimeOfDay(&nowLocal); + Unix2Amiga(&Globals->LastNtpSync, &last); + Utc2Local(&last, &lastLocal); + seconds = nowLocal.tv_secs - lastLocal.tv_secs; + + if (seconds > 60 * 60 * 24) + { + static const char *tooOld = "> 24 hours"; + GT_SetGadgetAttrs( + Globals->Window->Gadgets->LastSyncGadget, + Globals->Window->SettingWindow, NULL, + GTTX_Text, (IPTR)tooOld, + TAG_END); + + return; + } + + Amiga2Date(lastLocal.tv_secs, &clockdata); + dateTime.dat_Stamp.ds_Minute = clockdata.hour * 60 + clockdata.min; + dateTime.dat_Stamp.ds_Tick = clockdata.sec * 50; + DateToStr(&dateTime); + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->LastSyncGadget, + Globals->Window->SettingWindow, NULL, + GTTX_Text, (IPTR)label, + TAG_END); +} + +void SetDefaultSettings(void) +{ + static char priorityText[MAXLONGCHARSIZE]; + static char thresholdText[MAXLONGLONGCHARSIZE]; + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->ServerGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)DefaultSettings.DestinationAddress, + TAG_END); + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->PortGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)DefaultSettings.DestinationPort, + TAG_END); + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->TimeoutGadget, + Globals->Window->SettingWindow, NULL, + GTIN_Number, DefaultSettings.Timeout, + TAG_END); + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->IntervalGadget, + Globals->Window->SettingWindow, NULL, + GTIN_Number, DefaultSettings.Interval, + TAG_END); + + LongLongToStr(DefaultSettings.Threshold, thresholdText); + GT_SetGadgetAttrs( + Globals->Window->Gadgets->ThresholdGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)thresholdText, + TAG_END); + + GT_SetGadgetAttrs( + Globals->Window->Gadgets->VerboseGadget, + Globals->Window->SettingWindow, NULL, + GTCY_Active, DefaultSettings.Verbose, + TAG_END); + + LongToStr(DefaultSettings.Priority, priorityText); + GT_SetGadgetAttrs( + Globals->Window->Gadgets->PriorityGadget, + Globals->Window->SettingWindow, NULL, + GTST_String, (IPTR)priorityText, + TAG_END); +} diff --git a/win_main.c b/win_main.c new file mode 100644 index 0000000..23c35d6 --- /dev/null +++ b/win_main.c @@ -0,0 +1,343 @@ +/*- + * Copyright (c) 2017-2018 Carsten Sonne Larsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "message.h" +#include "state.h" +#include "time.h" +#include "mem.h" +#include "win.h" + +static void SettingsProc(void); +static void ProcessMsg(void); +static void HandleGadgetUp(struct Gadget *); + +void ShowSettingWindow(void) +{ + bool running; + + Forbid(); + running = Globals->Window->Task != NULL; + if (!running) + { + Globals->Window->Task = CreateNewProcTags( + NP_Entry, (IPTR)SettingsProc, + NP_Name, (IPTR)APP_SHORT_NAME " Window", + NP_StackSize, 64 * 1024, + TAG_DONE); + } + Permit(); + + if (running) + { + LogTrace("Setting process is already running"); + return; + } + + if (Globals->Window->Task != NULL) + { + LogTrace("Created setting process"); + } + else + { + LogError("Failed to create setting process"); + } +} + +void HideSettingWindow(void) +{ + Forbid(); + if (Globals->Window->Task != NULL) + { + Signal((void *)Globals->Window->Task, SIGBREAKF_CTRL_C); + } + Permit(); +} + +bool CheckSettingWindowClosed(void) +{ + return (Globals->Window->Task == NULL); +} + +static bool InitWindow(void) +{ + Globals->Window->Screen = LockPubScreen(NULL); + if (!Globals->Window->Screen) + { + return false; + } + + Globals->Window->UserPort = CreateMsgPort(); + if (Globals->Window->UserPort == NULL) + { + return false; + } + + Globals->Window->VisualInfo = GetVisualInfo(Globals->Window->Screen, NULL); + if (Globals->Window->VisualInfo == NULL) + { + return false; + } + + Globals->Window->PriorityText = AllocMemSafe(15); + Globals->Window->TimeZoneText = AllocMemSafe(10); + Globals->Window->ThresholdText = AllocMemSafe(MAXLONGLONGCHARSIZE); + if (Globals->Window->PriorityText == NULL || + Globals->Window->TimeZoneText == NULL || + Globals->Window->ThresholdText == NULL) + { + return false; + } + + LongToStr(Globals->Settings->Priority, Globals->Window->PriorityText); + LongLongToStr(Globals->Settings->Threshold, Globals->Window->ThresholdText); + GetTimeZoneText(Globals->Window->TimeZoneText); + + return true; +} + +static void DestroyWindow(void) +{ + if (Globals->Window->SettingWindow != NULL) + { + struct Message *msg; + while ((msg = GetMsg(Globals->Window->SettingWindow->UserPort))) + ReplyMsg(msg); + + CloseWindow(Globals->Window->SettingWindow); + Globals->Window->SettingWindow = NULL; + } + + if (Globals->Window->GadgetList != NULL) + { + FreeGadgets(Globals->Window->GadgetList); + Globals->Window->GadgetList = NULL; + } + + if (Globals->Window->VisualInfo != NULL) + { + FreeVisualInfo(Globals->Window->VisualInfo); + Globals->Window->VisualInfo = NULL; + } + + if (Globals->Window->UserPort != NULL) + { + struct Message *msg; + while ((msg = GetMsg(Globals->Window->UserPort))) + ReplyMsg(msg); + + DeleteMsgPort(Globals->Window->UserPort); + Globals->Window->UserPort = NULL; + } + + if (Globals->Window->Screen != NULL) + { + UnlockPubScreen(NULL, Globals->Window->Screen); + Globals->Window->Screen = NULL; + } + + FreeSettings(Globals->Window->Settings); + FreeMemSafe(Globals->Window->Gadgets); + FreeMemSafe(Globals->Window->PriorityText); + FreeMemSafe(Globals->Window->ThresholdText); + FreeMemSafe(Globals->Window->TimeZoneText); + Globals->Window->Gadgets = NULL; + Globals->Window->PriorityText = NULL; + Globals->Window->ThresholdText = NULL; + Globals->Window->TimeZoneText = NULL; + + Forbid(); + Globals->Window->Task = NULL; + Permit(); +} + +static bool CreateWindow(void) +{ + long w = Globals->Window->Screen->WBorLeft + Globals->Window->Screen->WBorRight + Globals->Window->Width + 38; // Why is this 38? + long h = Globals->Window->Height + 8 + Globals->Window->Screen->WBorBottom; + struct Window *x = OpenWindowTags(NULL, + WA_Width, w, + WA_Height, h, + WA_Left, (Globals->Window->Screen->Width - w) / 2, + WA_Top, (Globals->Window->Screen->Height - h) / 2, + WA_PubScreen, (IPTR)Globals->Window->Screen, + WA_Title, (IPTR)APP_LONG_NAME, + WA_Flags, WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_ACTIVATE | WFLG_SIMPLE_REFRESH, + WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_VANILLAKEY | IDCMP_GADGETUP | IDCMP_MENUPICK, + WA_Gadgets, (IPTR)Globals->Window->GadgetList, + TAG_END); + Globals->Window->SettingWindow = x; + if (!x) + { + return false; + } + + return true; +} + +static void InitWindowSettings() +{ + if (Globals->Window->Settings != NULL) + { + FreeSettings(Globals->Window->Settings); + } + Globals->Window->Settings = CopySettings(Globals->Settings); +} + +static void SyncExit() +{ + if (!Globals->ShuttingDown) + { + struct MsgPort *replyPort = CreateMsgPort(); + if (replyPort) + { + SendMessageTo(Globals->Broker->UserPort, replyPort, ATK_UNDO); + WaitPort(replyPort); + GetMsg(replyPort); + DeleteMsgPort(replyPort); + } + } +} + +static void SettingsProc(void) +{ + if (!InitWindow() || !CreateGadgets() || !CreateWindow()) + { + DestroyWindow(); + return; + } + + InitWindowSettings(); + ShowLastSync(); + ProcessMsg(); + SyncExit(); + DestroyWindow(); +} + +static void ProcessMsg(void) +{ + bool loop = true; + + GT_RefreshWindow(Globals->Window->SettingWindow, NULL); + + do + { + ULONG sigrcvd = Wait(SIGBREAKF_CTRL_C | + (1 << Globals->Window->UserPort->mp_SigBit) | + (1 << Globals->Window->SettingWindow->UserPort->mp_SigBit)); + + if (sigrcvd & (1 << Globals->Window->SettingWindow->UserPort->mp_SigBit)) + { + struct IntuiMessage *msg; + while ((msg = GT_GetIMsg(Globals->Window->SettingWindow->UserPort))) + { + switch (msg->Class) + { + case IDCMP_REFRESHWINDOW: + GT_BeginRefresh(Globals->Window->SettingWindow); + GT_EndRefresh(Globals->Window->SettingWindow, TRUE); + break; + case IDCMP_GADGETUP: + HandleGadgetUp((struct Gadget *)msg->IAddress); + break; + case IDCMP_VANILLAKEY: + if (msg->Code == 0x1b) + loop = false; + break; + case IDCMP_CLOSEWINDOW: + loop = false; + break; + } + + GT_ReplyIMsg(msg); + } + } + + if (sigrcvd & (1 << Globals->Window->UserPort->mp_SigBit)) + { + struct AppWindowMessage *msg; + while ((msg = (struct AppWindowMessage *)GetMsg(Globals->Window->UserPort))) + { + switch (msg->Type) + { + case ATK_REFRESH: + ShowLastSync(); + break; + default: + break; + } + ReplyMsg((struct Message *)msg); + } + } + + if (sigrcvd & SIGBREAKF_CTRL_C) + { + loop = false; + } + } while (loop); +} + +static void HandleGadgetUp(struct Gadget *gadget) +{ + switch (gadget->GadgetID) + { + case GID_SERVER: + SetServer(); + break; + case GID_PORT: + SetPort(); + break; + case GID_INTERVAL: + SetInterval(); + break; + case GID_TIMEOUT: + SetTimeout(); + break; + case GID_THRESHOLD: + SetThreshold(); + break; + case GID_VERBOSE: + SetVerbose(); + break; + case GID_PRIORITY: + SetCxPriority(); + break; + case GID_SAVE: + SendMessage(ATK_STORE); + break; + case GID_USE: + UseSettings(); + InitWindowSettings(); + SendMessage(ATK_APPLY); + break; + case GID_CANCEL: + SendMessage(ATK_UNDO); + HideSettingWindow(); + break; + default: + break; + } +}