snowid.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. *
  3. * MIT License
  4. *
  5. * Copyright (c) 2022 beyonddream
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. #include <limits.h>
  23. #include <stdbool.h>
  24. #include <stdint.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <inttypes.h>
  28. #include "snowid.h"
  29. #include "snowid_util.h"
  30. #include "snowid_checkpoint.h"
  31. /* private internal snowid state representation */
  32. typedef struct snow_state {
  33. /* Id will be generated only if true */
  34. bool enabled;
  35. /**
  36. * `checkpoint` is last time when an id was generated.
  37. * If we detect current time is < checkpoint, then
  38. * clock has moved backward and we refuse to generate
  39. * the id.
  40. */
  41. uint64_t checkpoint;
  42. /* 48-bits MAC address */
  43. uint64_t worker_id;
  44. /* 16 bit increments within same millisecond */
  45. uint16_t sequence_id;
  46. } snow_state_t;
  47. /**
  48. * Global variable to store the state.
  49. * Client should use some form of mutex if multiple threads are going to access the API's.
  50. */
  51. static snow_state_t *state;
  52. #define DEFAULT_CHECKPOINT_FILE_PATH "/data/snowid/timestamp.out"
  53. static bool get_checkpoint_mutable(uint64_t *out, char *timestamp_path);
  54. static bool get_worker_id_from_nw_if(uint64_t *out, char *interface);
  55. static bool get_snowid_to_binary(snow_id_binary_t out, const snow_id_t *snowid);
  56. static bool get_worker_id_from_nw_if(uint64_t *workerid, char *interface)
  57. {
  58. if (workerid == NULL) {
  59. return false;
  60. }
  61. return get_hw_addr_as_binary(workerid, interface);
  62. }
  63. static bool get_checkpoint_mutable(uint64_t *checkpoint, char *timestamp_path)
  64. {
  65. if (checkpoint == NULL) {
  66. return false;
  67. }
  68. *checkpoint = 0;
  69. if (timestamp_path == NULL) {
  70. timestamp_path = DEFAULT_CHECKPOINT_FILE_PATH;
  71. }
  72. bool success = true;
  73. FILE *file = fopen(timestamp_path, "r");
  74. if (file == NULL) {
  75. /* create a new file at timestamp_path */
  76. file = fopen(timestamp_path, "w");
  77. if (file == NULL) {
  78. fprintf(stderr, "Couldn't open timestamp_path for write.\n");
  79. success = false;
  80. } else {
  81. if (get_current_ts(checkpoint) == false) {
  82. fprintf(stderr, "Couldn't read current timestamp.\n");
  83. success = false;
  84. }
  85. if (*checkpoint == 0) {
  86. fprintf(stderr, "Checkpoint value seem to be zero.\n");
  87. success = false;
  88. }
  89. int ret = fwrite(checkpoint, sizeof(uint64_t), 1, file);
  90. if (ret != 1) {
  91. fprintf(stderr, "Couldn't write to timestamp_path.\n");
  92. success = false;
  93. }
  94. fclose(file);
  95. }
  96. } else {
  97. /* read from the existing file at timestamp_path */
  98. int ret = fread(checkpoint, sizeof(uint64_t), 1, file);
  99. if (ret != 1) {
  100. fprintf(stderr, "Couldn't read from timestamp_path.\n");
  101. success = false;
  102. }
  103. if (*checkpoint == 0) {
  104. fprintf(stderr, "Checkpoint value seem to be zero.\n");
  105. success = false;
  106. }
  107. fclose(file);
  108. }
  109. return success;
  110. }
  111. bool snow_get_id(snow_id_t *dest)
  112. {
  113. uint64_t current_time;
  114. if (dest == NULL) {
  115. return false;
  116. }
  117. if (state == NULL || state->enabled == false) {
  118. return false;
  119. }
  120. if (get_current_ts(&current_time) == false) {
  121. return false;
  122. }
  123. if (state->checkpoint > current_time) {
  124. fprintf(stderr, "Clock is running backwards.\n");
  125. state->enabled = false;
  126. return false;
  127. }
  128. if (state->checkpoint == current_time) {
  129. state->sequence_id++;
  130. } else {
  131. state->sequence_id = 0;
  132. }
  133. state->checkpoint = current_time;
  134. snow_id_t current = {
  135. .timestamp = state->checkpoint,
  136. .worker_id = state->worker_id,
  137. .sequence_id = state->sequence_id
  138. };
  139. *dest = current;
  140. return true;
  141. }
  142. static bool get_snowid_to_binary(snow_id_binary_t out, const snow_id_t *snowid)
  143. {
  144. int idx = 0;
  145. uint64_t timestamp = snowid->timestamp;
  146. uint64_t workerid = snowid->worker_id;
  147. uint16_t sequenceid = snowid->sequence_id;
  148. /* convert the timestamp into 64 bits */
  149. for(int8_t i = 7; i >= 0; i--) {
  150. out[idx++] = (timestamp >> (CHAR_BIT * i)) & 0xff;
  151. }
  152. /* convert the worker id into 48 bits */
  153. for(int8_t i = 5; i >= 0; i--) {
  154. out[idx++] = (workerid >> (CHAR_BIT * i)) & 0xff;
  155. }
  156. /* convert the sequence id into 16 bits */
  157. for(int8_t i = 1; i >= 0; i--) {
  158. out[idx++] = (sequenceid >> (CHAR_BIT * i)) & 0xff;
  159. }
  160. return true;
  161. }
  162. bool snow_id_convert(snow_id_binary_t out, const snow_id_t *snowid) {
  163. if (out == NULL || snowid == NULL) {
  164. return false;
  165. }
  166. return get_snowid_to_binary(out, snowid);
  167. }
  168. bool snow_get_id_as_binary(snow_id_binary_t dest_as_bin)
  169. {
  170. if (dest_as_bin == NULL) {
  171. return false;
  172. }
  173. snow_id_t dest;
  174. if (snow_get_id(&dest) == false) {
  175. return false;
  176. }
  177. return get_snowid_to_binary(dest_as_bin, &dest);
  178. }
  179. void snow_dump(FILE *stream)
  180. {
  181. if (state == NULL) {
  182. return;
  183. }
  184. if (stream == NULL) {
  185. stream = stdout;
  186. }
  187. #define LOG(KEY, VALUE) \
  188. do { \
  189. fprintf(stream, (KEY), (VALUE)); \
  190. } while(0)
  191. LOG("%s", "\n{");
  192. LOG("\"enabled\":%d,", state->enabled);
  193. LOG("\"worker_id\":%" PRIu64 ",", state->worker_id);
  194. LOG("\"checkpoint\":%" PRIu64 ",", state->checkpoint);
  195. LOG("\"sequence_id\":%hu", state->sequence_id);
  196. LOG("%s", "}\n");
  197. #undef LOG
  198. return;
  199. }
  200. void snow_init(snow_config_t *config)
  201. {
  202. uint64_t current_time;
  203. uint64_t checkpoint;
  204. uint64_t allowable_downtime;
  205. state = malloc(sizeof(snow_state_t));
  206. if (state == NULL) {
  207. fprintf(stderr, "malloc of snow_state_t failed.\n");
  208. return;
  209. }
  210. state->enabled = false;
  211. if (config == NULL) {
  212. fprintf(stderr, "snow config is NULL.\n");
  213. return;
  214. }
  215. /* if timestamp_path is new, then checkpoint can be current timestamp */
  216. if (get_checkpoint_mutable(&checkpoint, config->timestamp_path) == false) {
  217. return;
  218. }
  219. /* get the current timestamp again now */
  220. if (get_current_ts(&current_time) == false) {
  221. return;
  222. }
  223. if (checkpoint > current_time) {
  224. fprintf(stderr, "Clock is running backwards, failing to generate id.\n");
  225. return;
  226. }
  227. allowable_downtime = config->allowable_downtime;
  228. if ((current_time - checkpoint) > allowable_downtime) {
  229. fprintf(stderr, "Clock is too far advanced, failing to generate id.\n");
  230. return;
  231. }
  232. state->checkpoint = current_time;
  233. state->sequence_id = 0;
  234. if (get_worker_id_from_nw_if(&state->worker_id, config->interface) == false) {
  235. return;
  236. }
  237. /* start a child process to periodically save current time in `timestamp_path`*/
  238. snow_checkpoint_start(config->timestamp_path);
  239. /* init has succeeded */
  240. state->enabled = true;
  241. return;
  242. }
  243. void snow_shutdown(void)
  244. {
  245. free(state);
  246. state = NULL;
  247. return;
  248. }