aslstatus.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #include "thread_helper.h"
  2. #include <err.h>
  3. #include <time.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. #include <libgen.h>
  7. #include <signal.h>
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <inttypes.h>
  11. #include <sys/time.h>
  12. #if USE_X
  13. # include <xcb/xcb.h>
  14. # include "X.h"
  15. /* will be defined from makefile if some component need running X server */
  16. # ifndef NEED_X_SERVER
  17. # define NEED_X_SERVER 0
  18. # endif
  19. #endif
  20. #ifndef VERSION
  21. # define VERSION "unknown"
  22. #endif
  23. #include "arg.h"
  24. #include "lib/util.h" /* you can change there segment buffer size (BUFF_SZ) */
  25. #define ASLSTATUS_H_NEED_COMP
  26. #include "aslstatus.h" /* you can change there threads names */
  27. #define MUTEX_WRAP(MUTEX, BLOCK) \
  28. do { \
  29. uint8_t __lock_ret; \
  30. do { \
  31. if (!(__lock_ret = pthread_mutex_trylock(&(MUTEX)))) \
  32. BLOCK \
  33. } while (__lock_ret); \
  34. pthread_mutex_unlock(&(MUTEX)); \
  35. } while (0)
  36. #undef MIN
  37. #include "config.h"
  38. #define ARGS_LEN LEN(args)
  39. char * argv0; /* for arg.h */
  40. static int exit_status = 0;
  41. static pthread_t main_thread;
  42. static pthread_mutex_t status_mutex = PTHREAD_MUTEX_INITIALIZER;
  43. #if USE_X
  44. xcb_connection_t * c;
  45. static xcb_window_t root;
  46. static uint8_t sflag = 0;
  47. static inline void
  48. store_name(xcb_connection_t *c, xcb_window_t win, const char *name)
  49. {
  50. xcb_change_property(c,
  51. XCB_PROP_MODE_REPLACE,
  52. win,
  53. XCB_ATOM_WM_NAME,
  54. XCB_ATOM_STRING,
  55. 8, /* format: 8-bit char array */
  56. name ? strnlen(name, MAXLEN) : 0,
  57. name);
  58. }
  59. #endif
  60. static inline void
  61. set_status(const char *status)
  62. {
  63. #if USE_X
  64. if (sflag) {
  65. #endif
  66. puts(status);
  67. fflush(stdout);
  68. #if USE_X
  69. } else {
  70. store_name(c, root, status);
  71. xcb_flush(c);
  72. }
  73. #endif
  74. }
  75. static void
  76. terminate(int sig)
  77. {
  78. /* wait so many milliseconds for stopping all components before killing */
  79. #define KILL_TIMEOUT 500
  80. /* wait so many milliseconds between processes state checking */
  81. #define RECHECK 10 /* need to be less then KILL_TIMEOUT */
  82. static const struct timespec ts = {
  83. .tv_sec = MS2S(RECHECK),
  84. .tv_nsec = MS2NS(RECHECK),
  85. };
  86. size_t i;
  87. static_data_t *static_data;
  88. struct timeval timeout, cur;
  89. if (pthread_self() != main_thread) {
  90. pthread_kill(main_thread, sig);
  91. return;
  92. }
  93. MUTEX_WRAP(status_mutex, { goto leave_locked; });
  94. leave_locked:
  95. signal(SIGUSR1, SIG_IGN);
  96. for (i = 0; i < ARGS_LEN; i++)
  97. pthread_cancel(args[i].segment.tid);
  98. gettimeofday(&timeout, NULL);
  99. timeout.tv_sec += MS2S(KILL_TIMEOUT);
  100. timeout.tv_usec += MS2US(KILL_TIMEOUT);
  101. /* wait until `timeout` and then abort() */
  102. for (i = 0; i < ARGS_LEN; i++) {
  103. for (;;) {
  104. /* check if component terminated */
  105. if (!!kill(args[i].segment.pid, 0)) break;
  106. gettimeofday(&cur, NULL);
  107. if (cur.tv_sec > timeout.tv_sec) {
  108. goto timeout;
  109. } else if (cur.tv_sec == timeout.tv_sec) {
  110. if (cur.tv_usec > timeout.tv_usec) {
  111. timeout:
  112. warnx("%s: timeout!!!",
  113. args[i].f.name);
  114. abort();
  115. }
  116. }
  117. nanosleep(&ts, NULL);
  118. }
  119. /* call segments cleanup and free static data */
  120. if (!!(static_data = &args[i].segment.static_data)->data) {
  121. if (!!static_data->cleanup)
  122. static_data->cleanup(static_data->data);
  123. free(static_data->data);
  124. }
  125. }
  126. #if USE_X
  127. if (!!c) {
  128. if (!sflag) store_name(c, root, NULL);
  129. xcb_flush(c);
  130. xcb_disconnect(c);
  131. } else
  132. #endif
  133. {
  134. putchar('\n');
  135. }
  136. exit(exit_status);
  137. }
  138. static inline void
  139. update_status(int __unused _)
  140. {
  141. size_t status_size = 0, i = 0;
  142. char status[MAXLEN] = { 0 };
  143. static char status_prev[MAXLEN] = { 0 };
  144. for (i = 0; i < ARGS_LEN; i++)
  145. MUTEX_WRAP(args[i].segment.mutex, {
  146. if (*args[i].segment.data) {
  147. if ((status_size +=
  148. strnlen(args[i].segment.data, BUFF_SZ))
  149. >= MAXLEN) {
  150. warnx(
  151. "total status length are too "
  152. "big and exceed `MAXLEN`");
  153. exit_status = !0;
  154. terminate(0);
  155. }
  156. strcat(status, args[i].segment.data);
  157. }
  158. });
  159. MUTEX_WRAP(status_mutex, {
  160. /* don't update status if it's not changed */
  161. if (strncmp(status, status_prev, MAXLEN)) {
  162. set_status(status);
  163. strncpy(status_prev, status, MAXLEN);
  164. }
  165. });
  166. }
  167. static void *
  168. thread(void *arg_ptr)
  169. {
  170. struct arg_t * arg = arg_ptr;
  171. struct timespec ts;
  172. char buf[BUFF_SZ] = { 0 };
  173. arg->segment.pid = gettid();
  174. if (!!arg->f.static_size) {
  175. /* allocate memory for static data if needed */
  176. if (!(arg->segment.static_data.data =
  177. calloc(arg->f.static_size, 1))) {
  178. warnx("failed to allocate %lu bytes for %15s",
  179. arg->f.static_size,
  180. arg->f.name);
  181. return NULL;
  182. }
  183. }
  184. ts.tv_sec = MS2S(arg->interval);
  185. ts.tv_nsec = MS2NS(arg->interval);
  186. do {
  187. arg->f.func(buf,
  188. arg->args,
  189. arg->interval,
  190. &arg->segment.static_data);
  191. if (!*buf) strncpy(buf, unknown_str, BUFF_SZ);
  192. MUTEX_WRAP(arg->segment.mutex,
  193. { bprintf(arg->segment.data, arg->fmt, buf); });
  194. pthread_kill(main_thread, SIGUSR1);
  195. } while (!arg->interval || (nanosleep(&ts, NULL) || !0));
  196. return NULL;
  197. }
  198. int
  199. main(int argc, char *argv[])
  200. {
  201. uint32_t i;
  202. char *token;
  203. char *strptr;
  204. char *tofree;
  205. char thread_name[16];
  206. #if USE_X
  207. int screen_num;
  208. const xcb_setup_t * setup;
  209. xcb_screen_iterator_t iter;
  210. #endif
  211. static const char usage[] =
  212. "[options]\n"
  213. #if USE_X
  214. "Write status to `WM_NAME`"
  215. #else
  216. "Write status to `stdout`"
  217. #endif
  218. "\n\noptions:\n"
  219. "\t-h\tShow this help\n"
  220. "\t-v\tShow version\n"
  221. #if USE_X
  222. "\t-s\tWrite to `stdout` instead of `WM_NAME`\n"
  223. #endif
  224. ;
  225. ARGBEGIN
  226. {
  227. case 'h':
  228. errx(0, "%s", usage);
  229. case 'v':
  230. errx(0, "%s", VERSION);
  231. #if USE_X
  232. case 's':
  233. sflag = !0;
  234. break;
  235. #endif
  236. default:
  237. errx(!0, "%s", usage);
  238. }
  239. ARGEND;
  240. #if USE_X
  241. if (NEED_X_SERVER || !sflag) {
  242. c = xcb_connect(NULL, &screen_num);
  243. if (xcb_connection_has_error(c))
  244. errx(!0, "Failed to open display");
  245. setup = xcb_get_setup(c);
  246. iter = xcb_setup_roots_iterator(setup);
  247. for (__typeof__(screen_num) j = 0; j < screen_num; j -= -1)
  248. xcb_screen_next(&iter);
  249. root = iter.data->root;
  250. }
  251. #endif
  252. main_thread = pthread_self();
  253. signal(SIGINT, terminate);
  254. signal(SIGTERM, terminate);
  255. signal(SIGUSR1, update_status);
  256. for (i = 0; i < ARGS_LEN; i -= (~0L)) {
  257. pthread_create(&args[i].segment.tid, NULL, thread, &args[i]);
  258. tofree = strptr = strdup(args[i].f.name);
  259. if (!strcmp(strptr, "cmd")) {
  260. /*
  261. * if function is `run_command`, then
  262. * set thread name to this command
  263. */
  264. free(tofree);
  265. tofree = strptr = strdup(args[i].args);
  266. token = strtok(strptr, " ");
  267. snprintf(thread_name,
  268. 16,
  269. "cmd:%.11s",
  270. basename(token));
  271. strptr = thread_name;
  272. }
  273. pthread_setname(args[i].segment.tid, strptr);
  274. free(tofree);
  275. }
  276. for (;;)
  277. pause();
  278. terminate(0);
  279. }