luci-bwc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. /*
  2. * luci-bwc - Very simple bandwidth collector cache for LuCI realtime graphs
  3. *
  4. * Copyright (C) 2010 Jo-Philipp Wich <jow@openwrt.org>
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <stdint.h>
  22. #include <inttypes.h>
  23. #include <fcntl.h>
  24. #include <time.h>
  25. #include <errno.h>
  26. #include <unistd.h>
  27. #include <signal.h>
  28. #include <sys/stat.h>
  29. #include <sys/mman.h>
  30. #include <arpa/inet.h>
  31. #include <dlfcn.h>
  32. #include <iwinfo.h>
  33. #define STEP_COUNT 60
  34. #define STEP_TIME 1
  35. #define TIMEOUT 10
  36. #define PID_PATH "/var/run/luci-bwc.pid"
  37. #define DB_PATH "/var/lib/luci-bwc"
  38. #define DB_IF_FILE DB_PATH "/if/%s"
  39. #define DB_RD_FILE DB_PATH "/radio/%s"
  40. #define DB_CN_FILE DB_PATH "/connections"
  41. #define DB_LD_FILE DB_PATH "/load"
  42. #define IF_SCAN_PATTERN \
  43. " %[^ :]:%u %u" \
  44. " %*d %*d %*d %*d %*d %*d" \
  45. " %u %u"
  46. #define LD_SCAN_PATTERN \
  47. "%f %f %f"
  48. struct file_map {
  49. int fd;
  50. int size;
  51. char *mmap;
  52. };
  53. struct traffic_entry {
  54. uint32_t time;
  55. uint32_t rxb;
  56. uint32_t rxp;
  57. uint32_t txb;
  58. uint32_t txp;
  59. };
  60. struct conn_entry {
  61. uint32_t time;
  62. uint32_t udp;
  63. uint32_t tcp;
  64. uint32_t other;
  65. };
  66. struct load_entry {
  67. uint32_t time;
  68. uint16_t load1;
  69. uint16_t load5;
  70. uint16_t load15;
  71. };
  72. struct radio_entry {
  73. uint32_t time;
  74. uint16_t rate;
  75. uint8_t rssi;
  76. uint8_t noise;
  77. };
  78. static int readpid(void)
  79. {
  80. int fd;
  81. int pid = -1;
  82. char buf[9] = { 0 };
  83. if ((fd = open(PID_PATH, O_RDONLY)) > -1)
  84. {
  85. if (read(fd, buf, sizeof(buf)))
  86. {
  87. buf[8] = 0;
  88. pid = atoi(buf);
  89. }
  90. close(fd);
  91. }
  92. return pid;
  93. }
  94. static int writepid(void)
  95. {
  96. int fd;
  97. int wlen;
  98. char buf[9] = { 0 };
  99. if ((fd = open(PID_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0600)) > -1)
  100. {
  101. wlen = snprintf(buf, sizeof(buf), "%i", getpid());
  102. write(fd, buf, wlen);
  103. close(fd);
  104. return 0;
  105. }
  106. return -1;
  107. }
  108. static int timeout = TIMEOUT;
  109. static int countdown = -1;
  110. static void reset_countdown(int sig)
  111. {
  112. countdown = timeout;
  113. }
  114. static char *progname;
  115. static int prognamelen;
  116. static struct iwinfo_ops *backend = NULL;
  117. static int init_directory(char *path)
  118. {
  119. char *p = path;
  120. for (p = &path[1]; *p; p++)
  121. {
  122. if (*p == '/')
  123. {
  124. *p = 0;
  125. if (mkdir(path, 0700) && (errno != EEXIST))
  126. return -1;
  127. *p = '/';
  128. }
  129. }
  130. return 0;
  131. }
  132. static int init_file(char *path, int esize)
  133. {
  134. int i, file;
  135. char buf[sizeof(struct traffic_entry)] = { 0 };
  136. if (init_directory(path))
  137. return -1;
  138. if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
  139. {
  140. for (i = 0; i < STEP_COUNT; i++)
  141. {
  142. if (write(file, buf, esize) < 0)
  143. break;
  144. }
  145. close(file);
  146. return 0;
  147. }
  148. return -1;
  149. }
  150. static inline uint32_t timeof(void *entry)
  151. {
  152. return ntohl(((struct traffic_entry *)entry)->time);
  153. }
  154. static int update_file(const char *path, void *entry, int esize)
  155. {
  156. int rv = -1;
  157. int file;
  158. char *map;
  159. if ((file = open(path, O_RDWR)) >= 0)
  160. {
  161. map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
  162. MAP_SHARED | MAP_LOCKED, file, 0);
  163. if ((map != NULL) && (map != MAP_FAILED))
  164. {
  165. if (timeof(entry) > timeof(map + esize * (STEP_COUNT-1)))
  166. {
  167. memmove(map, map + esize, esize * (STEP_COUNT-1));
  168. memcpy(map + esize * (STEP_COUNT-1), entry, esize);
  169. }
  170. munmap(map, esize * STEP_COUNT);
  171. rv = 0;
  172. }
  173. close(file);
  174. }
  175. return rv;
  176. }
  177. static int mmap_file(const char *path, int esize, struct file_map *m)
  178. {
  179. m->fd = -1;
  180. m->size = -1;
  181. m->mmap = NULL;
  182. if ((m->fd = open(path, O_RDONLY)) >= 0)
  183. {
  184. m->size = STEP_COUNT * esize;
  185. m->mmap = mmap(NULL, m->size, PROT_READ,
  186. MAP_SHARED | MAP_LOCKED, m->fd, 0);
  187. if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
  188. return 0;
  189. }
  190. return -1;
  191. }
  192. static void umap_file(struct file_map *m)
  193. {
  194. if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
  195. munmap(m->mmap, m->size);
  196. if (m->fd > -1)
  197. close(m->fd);
  198. }
  199. static void * iw_open(void)
  200. {
  201. return dlopen("/usr/lib/libiwinfo.so", RTLD_LAZY);
  202. }
  203. static int iw_update(
  204. void *iw, const char *ifname, uint16_t *rate, uint8_t *rssi, uint8_t *noise
  205. ) {
  206. struct iwinfo_ops *(*probe)(const char *);
  207. int val;
  208. if (!backend)
  209. {
  210. probe = dlsym(iw, "iwinfo_backend");
  211. if (!probe)
  212. return 0;
  213. backend = probe(ifname);
  214. if (!backend)
  215. return 0;
  216. }
  217. *rate = (backend->bitrate && !backend->bitrate(ifname, &val)) ? val : 0;
  218. *rssi = (backend->signal && !backend->signal(ifname, &val)) ? val : 0;
  219. *noise = (backend->noise && !backend->noise(ifname, &val)) ? val : 0;
  220. return 1;
  221. }
  222. static void iw_close(void *iw)
  223. {
  224. void (*finish)(void);
  225. finish = dlsym(iw, "iwinfo_finish");
  226. if (finish)
  227. finish();
  228. dlclose(iw);
  229. }
  230. static int update_ifstat(
  231. const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
  232. ) {
  233. char path[1024];
  234. struct stat s;
  235. struct traffic_entry e;
  236. snprintf(path, sizeof(path), DB_IF_FILE, ifname);
  237. if (stat(path, &s))
  238. {
  239. if (init_file(path, sizeof(struct traffic_entry)))
  240. {
  241. fprintf(stderr, "Failed to init %s: %s\n",
  242. path, strerror(errno));
  243. return -1;
  244. }
  245. }
  246. e.time = htonl(time(NULL));
  247. e.rxb = htonl(rxb);
  248. e.rxp = htonl(rxp);
  249. e.txb = htonl(txb);
  250. e.txp = htonl(txp);
  251. return update_file(path, &e, sizeof(struct traffic_entry));
  252. }
  253. static int update_radiostat(
  254. const char *ifname, uint16_t rate, uint8_t rssi, uint8_t noise
  255. ) {
  256. char path[1024];
  257. struct stat s;
  258. struct radio_entry e;
  259. snprintf(path, sizeof(path), DB_RD_FILE, ifname);
  260. if (stat(path, &s))
  261. {
  262. if (init_file(path, sizeof(struct radio_entry)))
  263. {
  264. fprintf(stderr, "Failed to init %s: %s\n",
  265. path, strerror(errno));
  266. return -1;
  267. }
  268. }
  269. e.time = htonl(time(NULL));
  270. e.rate = htons(rate);
  271. e.rssi = rssi;
  272. e.noise = noise;
  273. return update_file(path, &e, sizeof(struct radio_entry));
  274. }
  275. static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
  276. {
  277. char path[1024];
  278. struct stat s;
  279. struct conn_entry e;
  280. snprintf(path, sizeof(path), DB_CN_FILE);
  281. if (stat(path, &s))
  282. {
  283. if (init_file(path, sizeof(struct conn_entry)))
  284. {
  285. fprintf(stderr, "Failed to init %s: %s\n",
  286. path, strerror(errno));
  287. return -1;
  288. }
  289. }
  290. e.time = htonl(time(NULL));
  291. e.udp = htonl(udp);
  292. e.tcp = htonl(tcp);
  293. e.other = htonl(other);
  294. return update_file(path, &e, sizeof(struct conn_entry));
  295. }
  296. static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
  297. {
  298. char path[1024];
  299. struct stat s;
  300. struct load_entry e;
  301. snprintf(path, sizeof(path), DB_LD_FILE);
  302. if (stat(path, &s))
  303. {
  304. if (init_file(path, sizeof(struct load_entry)))
  305. {
  306. fprintf(stderr, "Failed to init %s: %s\n",
  307. path, strerror(errno));
  308. return -1;
  309. }
  310. }
  311. e.time = htonl(time(NULL));
  312. e.load1 = htons(load1);
  313. e.load5 = htons(load5);
  314. e.load15 = htons(load15);
  315. return update_file(path, &e, sizeof(struct load_entry));
  316. }
  317. static int run_daemon(void)
  318. {
  319. FILE *info;
  320. uint32_t rxb, txb, rxp, txp;
  321. uint32_t udp, tcp, other;
  322. uint16_t rate;
  323. uint8_t rssi, noise;
  324. float lf1, lf5, lf15;
  325. char line[1024];
  326. char ifname[16];
  327. int i;
  328. void *iw;
  329. struct sigaction sa;
  330. struct stat s;
  331. const char *ipc = stat("/proc/net/nf_conntrack", &s)
  332. ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
  333. switch (fork())
  334. {
  335. case -1:
  336. perror("fork()");
  337. return -1;
  338. case 0:
  339. if (chdir("/") < 0)
  340. {
  341. perror("chdir()");
  342. exit(1);
  343. }
  344. close(0);
  345. close(1);
  346. close(2);
  347. break;
  348. default:
  349. return 0;
  350. }
  351. /* setup USR1 signal handler to reset timer */
  352. sa.sa_handler = reset_countdown;
  353. sa.sa_flags = SA_RESTART;
  354. sigemptyset(&sa.sa_mask);
  355. sigaction(SIGUSR1, &sa, NULL);
  356. /* write pid */
  357. if (writepid())
  358. {
  359. fprintf(stderr, "Failed to write pid file: %s\n", strerror(errno));
  360. return 1;
  361. }
  362. /* initialize iwinfo */
  363. iw = iw_open();
  364. /* go */
  365. for (reset_countdown(0); countdown >= 0; countdown--)
  366. {
  367. /* alter progname for ps, top */
  368. memset(progname, 0, prognamelen);
  369. snprintf(progname, prognamelen, "luci-bwc %d", countdown);
  370. if ((info = fopen("/proc/net/dev", "r")) != NULL)
  371. {
  372. while (fgets(line, sizeof(line), info))
  373. {
  374. if (strchr(line, '|'))
  375. continue;
  376. if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
  377. {
  378. if (strncmp(ifname, "lo", sizeof(ifname)))
  379. update_ifstat(ifname, rxb, rxp, txb, txp);
  380. }
  381. }
  382. fclose(info);
  383. }
  384. if (iw)
  385. {
  386. for (i = 0; i < 5; i++)
  387. {
  388. #define iw_checkif(pattern) \
  389. do { \
  390. snprintf(ifname, sizeof(ifname), pattern, i); \
  391. if (iw_update(iw, ifname, &rate, &rssi, &noise)) \
  392. { \
  393. update_radiostat(ifname, rate, rssi, noise); \
  394. continue; \
  395. } \
  396. } while(0)
  397. iw_checkif("wlan%d");
  398. iw_checkif("ath%d");
  399. iw_checkif("wl%d");
  400. }
  401. }
  402. if ((info = fopen(ipc, "r")) != NULL)
  403. {
  404. udp = 0;
  405. tcp = 0;
  406. other = 0;
  407. while (fgets(line, sizeof(line), info))
  408. {
  409. if (strstr(line, "TIME_WAIT"))
  410. continue;
  411. if (strstr(line, "src=127.0.0.1 ") &&
  412. strstr(line, "dst=127.0.0.1 "))
  413. continue;
  414. if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
  415. {
  416. if (!strcmp(ifname, "tcp"))
  417. tcp++;
  418. else if (!strcmp(ifname, "udp"))
  419. udp++;
  420. else
  421. other++;
  422. }
  423. }
  424. update_cnstat(udp, tcp, other);
  425. fclose(info);
  426. }
  427. if ((info = fopen("/proc/loadavg", "r")) != NULL)
  428. {
  429. if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
  430. {
  431. update_ldstat((uint16_t)(lf1 * 100),
  432. (uint16_t)(lf5 * 100),
  433. (uint16_t)(lf15 * 100));
  434. }
  435. fclose(info);
  436. }
  437. sleep(STEP_TIME);
  438. }
  439. unlink(PID_PATH);
  440. if (iw)
  441. iw_close(iw);
  442. return 0;
  443. }
  444. static void check_daemon(void)
  445. {
  446. int pid;
  447. if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
  448. {
  449. /* daemon ping failed, try to start it up */
  450. if (run_daemon())
  451. {
  452. fprintf(stderr,
  453. "Failed to ping daemon and unable to start it up: %s\n",
  454. strerror(errno));
  455. exit(1);
  456. }
  457. }
  458. else if (kill(pid, SIGUSR1))
  459. {
  460. fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
  461. exit(2);
  462. }
  463. }
  464. static int run_dump_ifname(const char *ifname)
  465. {
  466. int i;
  467. char path[1024];
  468. struct file_map m;
  469. struct traffic_entry *e;
  470. check_daemon();
  471. snprintf(path, sizeof(path), DB_IF_FILE, ifname);
  472. if (mmap_file(path, sizeof(struct traffic_entry), &m))
  473. {
  474. fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
  475. return 1;
  476. }
  477. for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
  478. {
  479. e = (struct traffic_entry *) &m.mmap[i];
  480. if (!e->time)
  481. continue;
  482. printf("[ %u, %u, %" PRIu32
  483. ", %u, %u ]%s\n",
  484. ntohl(e->time),
  485. ntohl(e->rxb), ntohl(e->rxp),
  486. ntohl(e->txb), ntohl(e->txp),
  487. ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
  488. }
  489. umap_file(&m);
  490. return 0;
  491. }
  492. static int run_dump_radio(const char *ifname)
  493. {
  494. int i;
  495. char path[1024];
  496. struct file_map m;
  497. struct radio_entry *e;
  498. check_daemon();
  499. snprintf(path, sizeof(path), DB_RD_FILE, ifname);
  500. if (mmap_file(path, sizeof(struct radio_entry), &m))
  501. {
  502. fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
  503. return 1;
  504. }
  505. for (i = 0; i < m.size; i += sizeof(struct radio_entry))
  506. {
  507. e = (struct radio_entry *) &m.mmap[i];
  508. if (!e->time)
  509. continue;
  510. printf("[ %u, %d, %d, %d ]%s\n",
  511. ntohl(e->time),
  512. e->rate, e->rssi, e->noise,
  513. ((i + sizeof(struct radio_entry)) < m.size) ? "," : "");
  514. }
  515. umap_file(&m);
  516. return 0;
  517. }
  518. static int run_dump_conns(void)
  519. {
  520. int i;
  521. char path[1024];
  522. struct file_map m;
  523. struct conn_entry *e;
  524. check_daemon();
  525. snprintf(path, sizeof(path), DB_CN_FILE);
  526. if (mmap_file(path, sizeof(struct conn_entry), &m))
  527. {
  528. fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
  529. return 1;
  530. }
  531. for (i = 0; i < m.size; i += sizeof(struct conn_entry))
  532. {
  533. e = (struct conn_entry *) &m.mmap[i];
  534. if (!e->time)
  535. continue;
  536. printf("[ %u, %u, %u, %u ]%s\n",
  537. ntohl(e->time), ntohl(e->udp),
  538. ntohl(e->tcp), ntohl(e->other),
  539. ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
  540. }
  541. umap_file(&m);
  542. return 0;
  543. }
  544. static int run_dump_load(void)
  545. {
  546. int i;
  547. char path[1024];
  548. struct file_map m;
  549. struct load_entry *e;
  550. check_daemon();
  551. snprintf(path, sizeof(path), DB_LD_FILE);
  552. if (mmap_file(path, sizeof(struct load_entry), &m))
  553. {
  554. fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
  555. return 1;
  556. }
  557. for (i = 0; i < m.size; i += sizeof(struct load_entry))
  558. {
  559. e = (struct load_entry *) &m.mmap[i];
  560. if (!e->time)
  561. continue;
  562. printf("[ %u, %u, %u, %u ]%s\n",
  563. ntohl(e->time),
  564. ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
  565. ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
  566. }
  567. umap_file(&m);
  568. return 0;
  569. }
  570. int main(int argc, char *argv[])
  571. {
  572. int opt;
  573. progname = argv[0];
  574. prognamelen = -1;
  575. for (opt = 0; opt < argc; opt++)
  576. prognamelen += 1 + strlen(argv[opt]);
  577. while ((opt = getopt(argc, argv, "t:i:r:cl")) > -1)
  578. {
  579. switch (opt)
  580. {
  581. case 't':
  582. timeout = atoi(optarg);
  583. break;
  584. case 'i':
  585. if (optarg)
  586. return run_dump_ifname(optarg);
  587. break;
  588. case 'r':
  589. if (optarg)
  590. return run_dump_radio(optarg);
  591. break;
  592. case 'c':
  593. return run_dump_conns();
  594. case 'l':
  595. return run_dump_load();
  596. default:
  597. break;
  598. }
  599. }
  600. fprintf(stderr,
  601. "Usage:\n"
  602. " %s [-t timeout] -i ifname\n"
  603. " %s [-t timeout] -r radiodev\n"
  604. " %s [-t timeout] -c\n"
  605. " %s [-t timeout] -l\n",
  606. argv[0], argv[0], argv[0], argv[0]
  607. );
  608. return 1;
  609. }