PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/gmcpubar.c

https://github.com/vmj/gmbar
C | 360 lines | 278 code | 48 blank | 34 comment | 31 complexity | a420d1a9b42ab87d7f8211e1e348d69f MD5 | raw file
  1. #include <stdlib.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include "libgmbar.h"
  5. #include "common.h"
  6. #include "log.h"
  7. #include "readfile.h"
  8. #include "buffer.h"
  9. #include "version.h"
  10. /* Maximum length of a CPU field label in /proc/stat ('cpu[CPU_INDEX] ') */
  11. #define MAX_CPU_FIELD_LEN 20
  12. /* Static functions */
  13. static error_t handle_option(int key,
  14. char* arg,
  15. struct argp_state *state);
  16. static int get_stat(buffer* stat,
  17. const char* field,
  18. unsigned int *kern,
  19. unsigned int *user,
  20. unsigned int *nice,
  21. unsigned int *idle);
  22. static long get_num_cpus();
  23. static long parse_cpuinfo(const char* cpuinfo,
  24. const unsigned int size);
  25. static int parse_stat(const char* stat,
  26. const unsigned int size,
  27. const char* field,
  28. unsigned int *kern,
  29. unsigned int *user,
  30. unsigned int *nice,
  31. unsigned int *idle);
  32. static unsigned int parse_unsigned_int(const char* str,
  33. const char** end);
  34. /* Argp option keys (available: 'a'-'f') */
  35. enum {
  36. OPTION_KERN_COLOR = 'a',
  37. OPTION_USER_COLOR = 'b',
  38. OPTION_NICE_COLOR = 'c',
  39. OPTION_IDLE_COLOR = 'd',
  40. OPTION_CPU_INDEX = 'f',
  41. };
  42. /* Argp input */
  43. typedef struct arguments arguments;
  44. struct arguments {
  45. common_arguments common_config;
  46. int cpu_index;
  47. };
  48. /* Options */
  49. static struct argp_option options[] = {
  50. { "kern", OPTION_KERN_COLOR, "COLOR", 0,
  51. "Color for the kernel portion of the bar" },
  52. { "user", OPTION_USER_COLOR, "COLOR", 0,
  53. "Color for the user portion of the bar" },
  54. { "nice", OPTION_NICE_COLOR, "COLOR", 0,
  55. "Color for the nice portion of the bar" },
  56. { "idle", OPTION_IDLE_COLOR, "COLOR", 0,
  57. "Color for the idle portion of the bar" },
  58. { "cpu", OPTION_CPU_INDEX, "INDEX", 0,
  59. "Index of the processor to watch" },
  60. { 0 }
  61. };
  62. /* Argp parser */
  63. static const struct argp argp = { options, handle_option, NULL,
  64. "gmcpubar -- dzen2 cpu bar",
  65. common_argp_child
  66. };
  67. int
  68. main(int argc, char** argv)
  69. {
  70. int err = 0;
  71. long total; // Number of clock ticks per second
  72. long num_cpus;
  73. char cpu_field[MAX_CPU_FIELD_LEN + 1];
  74. unsigned int _kern, _user, _nice, _idle;
  75. unsigned int kern, user, nice, idle;
  76. arguments config;
  77. buffer* stat = NULL;
  78. gmbar* bar = NULL;
  79. stat = buffer_new();
  80. if (!stat)
  81. {
  82. return -1;
  83. }
  84. bar = gmbar_new_with_defaults(100, 10, "red", "none");
  85. if (!bar)
  86. {
  87. buffer_free(stat);
  88. return -1;
  89. }
  90. err = gmbar_add_sections(bar, 4, "red", "orange", "yellow", "none");
  91. if (err)
  92. {
  93. buffer_free(stat);
  94. gmbar_free(bar);
  95. return -1;
  96. }
  97. config.cpu_index = -1;
  98. config.common_config.bar = bar;
  99. config.common_config.interval = 15;
  100. config.common_config.prefix = NULL;
  101. config.common_config.suffix = NULL;
  102. err = argp_parse(&argp, argc, argv, 0, NULL, &config);
  103. if (err)
  104. {
  105. buffer_free(stat);
  106. gmbar_free(bar);
  107. return err;
  108. }
  109. /* Clock ticks per second (per CPU) */
  110. total = sysconf(_SC_CLK_TCK);
  111. /* Number of CPUs */
  112. num_cpus = get_num_cpus();
  113. if (num_cpus < 0)
  114. {
  115. buffer_free(stat);
  116. gmbar_free(bar);
  117. return num_cpus;
  118. }
  119. if (config.cpu_index >= 0
  120. && config.cpu_index <= CHAR_MAX
  121. && config.cpu_index < num_cpus)
  122. {
  123. snprintf(cpu_field, MAX_CPU_FIELD_LEN, "cpu%i ", config.cpu_index);
  124. }
  125. else
  126. {
  127. snprintf(cpu_field, MAX_CPU_FIELD_LEN, "cpu ");
  128. }
  129. /* Initialize history */
  130. err = get_stat(stat, cpu_field, &_kern, &_user, &_nice, &_idle);
  131. if (err)
  132. {
  133. buffer_free(stat);
  134. gmbar_free(bar);
  135. return err;
  136. }
  137. while (config.common_config.interval)
  138. {
  139. sleep(config.common_config.interval);
  140. err = get_stat(stat, cpu_field, &kern, &user, &nice, &idle);
  141. if (err)
  142. {
  143. buffer_free(stat);
  144. gmbar_free(bar);
  145. return err;
  146. }
  147. /* total is not accurate */
  148. total = (kern - _kern) + (user - _user) + (nice - _nice) + (idle - _idle);
  149. gmbar_set_section_width(bar->sections[0], total, kern - _kern);
  150. gmbar_set_section_width(bar->sections[1], total, user - _user);
  151. gmbar_set_section_width(bar->sections[2], total, nice - _nice);
  152. gmbar_set_section_width(bar->sections[3], total, idle - _idle);
  153. err = print_bar(&config.common_config);
  154. if (err)
  155. {
  156. buffer_free(stat);
  157. gmbar_free(bar);
  158. return err;
  159. }
  160. _kern = kern;
  161. _user = user;
  162. _nice = nice;
  163. _idle = idle;
  164. }
  165. return 0;
  166. }
  167. static error_t
  168. handle_option(int key, char* arg, struct argp_state *state)
  169. {
  170. error_t err = 0;
  171. arguments* config = (arguments*) state->input;
  172. switch (key)
  173. {
  174. case ARGP_KEY_INIT:
  175. state->child_inputs[0] = config;
  176. break;
  177. case OPTION_KERN_COLOR:
  178. err = parse_option_arg_string(arg, &config->common_config.bar->sections[0]->color);
  179. break;
  180. case OPTION_USER_COLOR:
  181. err = parse_option_arg_string(arg, &config->common_config.bar->sections[1]->color);
  182. break;
  183. case OPTION_NICE_COLOR:
  184. err = parse_option_arg_string(arg, &config->common_config.bar->sections[2]->color);
  185. break;
  186. case OPTION_IDLE_COLOR:
  187. err = parse_option_arg_string(arg, &config->common_config.bar->sections[3]->color);
  188. break;
  189. case OPTION_CPU_INDEX:
  190. config->cpu_index = parse_unsigned_int(arg, NULL);
  191. break;
  192. default:
  193. err = ARGP_ERR_UNKNOWN;
  194. break;
  195. }
  196. return err;
  197. }
  198. static int
  199. get_stat(buffer* stat,
  200. const char* field,
  201. unsigned int *kern,
  202. unsigned int *user,
  203. unsigned int *nice,
  204. unsigned int *idle)
  205. {
  206. int err = 0;
  207. *kern = *user = *nice = *idle = 0;
  208. /* reuse all the space */
  209. stat->len = 0;
  210. err = readfile("/proc/stat", &stat->buf, &stat->len, &stat->max);
  211. if (err)
  212. {
  213. return err;
  214. }
  215. err = parse_stat(stat->buf, stat->len, field, kern, user, nice, idle);
  216. return err;
  217. }
  218. static long
  219. get_num_cpus()
  220. {
  221. int err = 0;
  222. char* cpuinfo = NULL;
  223. unsigned int size = 0;
  224. unsigned int max = 0;
  225. err = readfile("/proc/cpuinfo", &cpuinfo, &size, &max);
  226. if (err || !cpuinfo)
  227. {
  228. return err;
  229. }
  230. return parse_cpuinfo(cpuinfo, size);
  231. }
  232. /**
  233. *
  234. */
  235. static long
  236. parse_cpuinfo(const char* cpuinfo, const unsigned int size)
  237. {
  238. const char* p = cpuinfo;
  239. unsigned int len = size;
  240. long cpus = 0;
  241. do
  242. {
  243. p = memstr(p, "processor", len);
  244. if (p && (p == cpuinfo || p[-1] == '\n'))
  245. {
  246. cpus++;
  247. len = size - (p - cpuinfo);
  248. p++;
  249. }
  250. } while (p);
  251. return cpus;
  252. }
  253. /**
  254. * Parse CPU field from stat.
  255. *
  256. * @param stat Contents of the /proc/stat file
  257. * @param size Size of the content
  258. * @param field Name of the field to parse, e.g. "cpu" or "cpu1", zero terminated
  259. * @param kern On return, contains the parsed value or zero.
  260. * @param user On return, contains the parsed value or zero.
  261. * @param nice On return, contains the parsed value or zero.
  262. * @param idle On return, contains the parsed value or zero.
  263. * @return Zero on success, -1 if the field is not found. Note that any
  264. * parse errors are not detected.
  265. */
  266. static int
  267. parse_stat(const char* stat,
  268. const unsigned int size,
  269. const char* field,
  270. unsigned int *kern,
  271. unsigned int *user,
  272. unsigned int *nice,
  273. unsigned int *idle)
  274. {
  275. const char* p = stat;
  276. unsigned int len = size;
  277. /* Initialize to zeros */
  278. *kern = *user = *nice = *idle = 0;
  279. /* Find the field */
  280. do
  281. {
  282. p = memstr(p, field, len);
  283. if (!p)
  284. {
  285. log_error("Field not found: %d", -1);
  286. return -1;
  287. }
  288. } while (p != stat && p[-1] != '\n' && p++ && (len = size - (p - stat)));
  289. /* Skip the label */
  290. p += strlen(field);
  291. *user = parse_unsigned_int(p, &p);
  292. *nice = parse_unsigned_int(p, &p);
  293. *kern = parse_unsigned_int(p, &p);
  294. *idle = parse_unsigned_int(p, NULL);
  295. return 0;
  296. }
  297. /**
  298. * @param str String to parse
  299. * @return Parsed value.
  300. */
  301. static unsigned int
  302. parse_unsigned_int(const char* str, const char** end)
  303. {
  304. unsigned int value = 0;
  305. while (!isdigit(*str))
  306. str++;
  307. while(isdigit(*str))
  308. value = value * 10 + (*str++ - '0');
  309. if (end)
  310. *end = (char*)str;
  311. return value;
  312. }