From 3d780676b58ffba269803a1b25fbcdfaa8033be7 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Fri, 21 May 2021 15:39:41 -0500 Subject: [PATCH] Finish rig_cookie Not implemented anywhere yet but functional --- include/hamlib/rig.h | 9 ++- src/rig.c | 154 ++++++++++++++++++++++++++----------------- tests/Makefile.am | 10 ++- tests/testcookie.c | 87 ++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 64 deletions(-) create mode 100644 tests/testcookie.c diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index fbba054ae..61dc582ae 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -106,6 +106,13 @@ __BEGIN_DECLS +/** + * \brief size of cookie request buffer + * Minimum size of cookie buffer to pass to rig_cookie + */ +// cookie is 26-char time code plus 10-char (2^31-1) random number +#define HAMLIB_COOKIE_SIZE 37 + //! @cond Doxygen_Suppress extern HAMLIB_EXPORT_VAR(const char) hamlib_version[]; extern HAMLIB_EXPORT_VAR(const char) hamlib_copyright[]; @@ -3047,7 +3054,7 @@ extern HAMLIB_EXPORT(int) rig_get_cache(RIG *rig, vfo_t vfo, freq_t *freq, int * typedef unsigned long rig_useconds_t; extern HAMLIB_EXPORT(int) hl_usleep(rig_useconds_t msec); -extern HAMLIB_EXPORT(char *) rig_cookie(char *cookie, enum cookie_e cookie_cmd); +extern HAMLIB_EXPORT(int) rig_cookie(RIG *rig, enum cookie_e cookie_cmd, char *cookie, int cookie_len); //! @endcond diff --git a/src/rig.c b/src/rig.c index d7e1622e7..8c63ab8ee 100644 --- a/src/rig.c +++ b/src/rig.c @@ -6473,82 +6473,116 @@ const char *HAMLIB_API rig_copyright() * RIG_COOKIE_GET must have cookie=NULL or NULL returned * RIG_COOKIE_RENEW must have cookie!=NULL or NULL returned * RIG_COOKIE_RELEASE must have cookie!=NULL or NULL returned; + * Cookies should only be used when needed to keep commands sequenced correctly + * For example, when setting both VFOA and VFOB frequency and mode + * Example to wait for cookie, do rig commands, and release + * while((cookie=rig_cookie(NULL, RIG_COOKIE_GET)) == NULL) hl_usleep(10*1000); + * set_freq A;set mode A;set freq B;set modeB; + * rig_cookie(cookie,RIG_COOKIE_RELEASE); */ -char *rig_cookie(char *cookie, enum cookie_e cookie_cmd) +int HAMLIB_API rig_cookie(RIG *rig, enum cookie_e cookie_cmd, char *cookie, + int cookie_len) { - static char cookie_save[32]; // only one client can have the cookie - double time_curr, time_last_used = 0; + // only 1 client can have the cookie so these can be static + // this should also prevent problems with DLLs & shared libraies + // the debug_msg is another non-thread-safe which this will help fix + // 27 char cookie will last until the year 10000 + static char cookie_save[HAMLIB_COOKIE_SIZE]; // only one client can have the 26-char cookie + static double time_last_used; + double time_curr; struct timespec tp; + if (cookie_len < 27) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): cookie_len < 32 so returning NULL!!\n", + __FILE__, __LINE__); + return -RIG_EINTERNAL; + } + switch (cookie_cmd) { - case RIG_COOKIE_RELEASE: - if (cookie == NULL) { - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): coookie NULL so nothing to do\n", __FILE__, __LINE__); - return NULL; // nothing to do - } - if (strcmp(cookie,cookie_save)==0) - { - cookie_save[0] = 0; - return NULL; - } - break; - } - if (cookie == NULL && cookie_cmd == RIG_COOKIE_GET) - { - // asking for a cookie but somebody may already have it - // if we already have a cookie and somebody asks see if we should expire the old one - printf("%d cookie_cmd=%d\n", (int)strlen(cookie_save), cookie_cmd); + case RIG_COOKIE_RELEASE: + if (cookie == NULL) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): coookie NULL so nothing to do\n", + __FILE__, __LINE__); + return -RIG_EINVAL; // nothing to do + } + + if (strcmp(cookie, cookie_save) == 0) // matching cookie so we'll clear it + { + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): %s coookie released\n", + __FILE__, __LINE__, cookie_save); + memset(cookie_save, 0, sizeof(cookie_save)); + return RIG_OK; + } + else // not the right cookie!! + { + rig_debug(RIG_DEBUG_ERR, + "%s(%d): %s can't release cookie as cookie %s is active\n", __FILE__, __LINE__, + cookie, cookie_save); + return -RIG_BUSBUSY; + } + + break; + + case RIG_COOKIE_RENEW: + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): %s comparing renew request to %s==%d\n", + __FILE__, __LINE__, cookie, cookie_save, strcmp(cookie, cookie_save)); + + if (strcmp(cookie, cookie_save) == 0) // matching cookie so we'll renew it + { + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) %s renew request granted\n", __FILE__, + __LINE__, cookie); + clock_gettime(CLOCK_REALTIME, &tp); + time_last_used = tp.tv_sec + tp.tv_nsec / 1e9; + return RIG_OK; + } + + rig_debug(RIG_DEBUG_ERR, + "%s(%d): %s renew request refused %s is active\n", + __FILE__, __LINE__, cookie, cookie_save); + return -RIG_EINVAL; // wrong cookie + + break; + + case RIG_COOKIE_GET: + // the way we expire cookies is if somebody else asks for one and the last renewal is > 1 second ago + // a polite client will have released the cookie + // we are just allow for a crashed client that fails to release:q clock_gettime(CLOCK_REALTIME, &tp); time_curr = tp.tv_sec + tp.tv_nsec / 1e9; - rig_debug(RIG_DEBUG_ERR, "%s(%d): time_curr=%f\n", __FILE__, __LINE__, - time_curr); - if (time_curr - time_last_used < 1) + if ((strcmp(cookie_save, cookie) == 0) + && (time_curr - time_last_used < 1)) // then we will deny the request { - printf("Cookie in use\n"); - rig_debug(RIG_DEBUG_ERR, "%s(%d): cookie==NULL but not RIG_COOKIE_GET\n", - __FILE__, __LINE__); - return NULL; + printf("Cookie %s in use\n", cookie_save); + rig_debug(RIG_DEBUG_ERR, "%s(%d): %s cookie is in use\n", __FILE__, __LINE__, + cookie_save); + return -RIG_BUSBUSY; + } + + if (cookie_save[0] != 0) + { + rig_debug(RIG_DEBUG_ERR, + "%s(%d): %s cookie has expired after %.3f seconds....overriding with new cookie\n", + __FILE__, __LINE__, cookie_save, time_curr - time_last_used); } - else if (strlen(cookie_save)!=0) - { - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): cookie expired so we will grant\n", __FILE__, __LINE__); - cookie_save[0] = 0; - } - } - if (strlen(cookie_save) == 0 && cookie_cmd == RIG_COOKIE_GET) - { date_strget(cookie_save, sizeof(cookie_save)); - clock_gettime(CLOCK_REALTIME, &tp); - time_curr = time_last_used = tp.tv_sec + tp.tv_nsec / 1e9; - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): cookie %s granted, time_curr=%f\n", - __FILE__, __LINE__, cookie_save, time_curr); - return cookie_save; - } - else if (strlen(cookie_save) == 0) // NULL should only be for GET - { - rig_debug(RIG_DEBUG_ERR, "%s(%d): cookie==NULL but not RIG_COOKIE_GET\n", - __FILE__, __LINE__); - return NULL; - } - else if (cookie_cmd == RIG_COOKIE_RENEW) - { - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): cookie %s renewed time_curr=%g\n", - __FILE__, __LINE__, - cookie, time_curr); - return cookie; - } - else if (cookie_cmd == RIG_COOKIE_RELEASE) - { - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): cookie %s released\n", __FILE__, __LINE__, - cookie); - cookie_save[0] = 0; + // add on our random number to ensure uniqueness + snprintf(cookie, cookie_len, "%s %ld\n", cookie_save, random()); + strcpy(cookie_save, cookie); + time_last_used = time_curr; + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): %s new cookie request granted\n", + __FILE__, __LINE__, cookie_save); + return RIG_OK; + break; + } rig_debug(RIG_DEBUG_ERR, "%s(%d): unknown condition!!\n'", __FILE__, __LINE__); - return NULL; + return -RIG_EPROTO; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 7d7a835e1..e8aa5cf1d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,7 +8,7 @@ DISTCLEANFILES = rigctl.log rigctl.sum testbcd.log testbcd.sum bin_PROGRAMS = rigctl rigctld rigmem rigsmtr rigswr rotctl rotctld rigctlcom ampctl ampctld -check_PROGRAMS = dumpmem testrig testrigopen testrigcaps testtrn testbcd testfreq listrigs testloc rig_bench testcache cachetest cachetest2 +check_PROGRAMS = dumpmem testrig testrigopen testrigcaps testtrn testbcd testfreq listrigs testloc rig_bench testcache cachetest cachetest2 testcookie RIGCOMMONSRC = rigctl_parse.c rigctl_parse.h dumpcaps.c uthash.h ROTCOMMONSRC = rotctl_parse.c rotctl_parse.h dumpcaps_rot.c uthash.h @@ -76,7 +76,7 @@ endif EXTRA_DIST = rigmatrix_head.html rig_split_lst.awk testctld.pl testrotctld.pl # Support 'make check' target for simple tests -check_SCRIPTS = testrig.sh testfreq.sh testbcd.sh testloc.sh testrigcaps.sh testcache.sh +check_SCRIPTS = testrig.sh testfreq.sh testbcd.sh testloc.sh testrigcaps.sh testcache.sh testcookie.sh TESTS = $(check_SCRIPTS) @@ -105,4 +105,8 @@ testcache.sh: echo './testcache 1' > testcache.sh chmod +x ./testcache.sh -CLEANFILES = testrig.sh testfreq.sh testbcd.sh testloc.sh testrigcaps.sh testcache.sh +testcookie.sh: + echo './testcookie 1' > testcookie.sh + chmod +x ./testcookie.sh + +CLEANFILES = testrig.sh testfreq.sh testbcd.sh testloc.sh testrigcaps.sh testcache.sh testcookie.sh diff --git a/tests/testcookie.c b/tests/testcookie.c new file mode 100644 index 000000000..fee207894 --- /dev/null +++ b/tests/testcookie.c @@ -0,0 +1,87 @@ +#include + +// GET tests +int test1() +{ + int retcode; + // Normal get + char cookie[HAMLIB_COOKIE_SIZE]; + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie, sizeof(cookie)); + + if (retcode == RIG_OK) { printf("Test#1a OK\n"); } + else {printf("Test#1a Failed\n"); return 1;} + + // Should be able to release and get it right back + rig_cookie(NULL, RIG_COOKIE_RELEASE, cookie, sizeof(cookie)); + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie, sizeof(cookie)); + + if (retcode == RIG_OK) { printf("Test#1b OK\n"); } + else {printf("Test#1b Failed\n"); return 1;} + + + // Doing a get when another cookie is active should fail + char cookie2[HAMLIB_COOKIE_SIZE]; + cookie2[0] = 0; + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie2, sizeof(cookie2)); + + if (retcode == RIG_OK) { printf("Test#1c OK\n"); } + else {printf("Test#1c Failed\n"); return 1;} + + // after 1 second we should be able to get a coookie + // this means the cookie holder did not renew within 1 second + hl_usleep(1500 * 1000); // after 1 second we should be able to get a coookie + + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie2, sizeof(cookie2)); + + if (retcode == RIG_OK) { printf("Test#1d OK\n"); } + else {printf("Test#1d Failed\n"); return 1;} + + retcode = rig_cookie(NULL, RIG_COOKIE_RELEASE, cookie2, sizeof(cookie2)); + + if (retcode == RIG_OK) { printf("Test#1e OK\n"); } + else {printf("Test#1e Failed\n"); return 1;} + + return 0; +} + +// RENEW tests +int test2() +{ + int retcode; + char cookie[HAMLIB_COOKIE_SIZE]; + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie, sizeof(cookie)); + + if (retcode == RIG_OK) { printf("Test#2a OK %s\n", cookie); } + else {printf("Test#2a Failed\n"); return 1;} + + retcode = rig_cookie(NULL, RIG_COOKIE_RELEASE, cookie, sizeof(cookie)); + + if (retcode == RIG_OK) { printf("Test#2b OK\n"); } + else {printf("Test#2b Failed\n"); return 1;} + +// get another cookie should work + char cookie2[HAMLIB_COOKIE_SIZE]; + retcode = rig_cookie(NULL, RIG_COOKIE_GET, cookie2, sizeof(cookie2)); + + if (retcode == RIG_OK) { printf("Test#2c OK %s\n", cookie2); } + else {printf("Test#2c Failed\n"); return 1;} + +// should not be able to renew 1st cookie + retcode = rig_cookie(NULL, RIG_COOKIE_RENEW, cookie, sizeof(cookie)); + + if (retcode != RIG_OK) { printf("Test#2d OK\n"); } + else {printf("Test#2d Failed cookie=%s\n", cookie); return 1;} + return 0; +} + + +int main() +{ + rig_set_debug(RIG_DEBUG_VERBOSE); + + if (test1()) { return 1; } + + if (test2()) { return 1; } + + return 0; +}