1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57#include "libbb.h"
58#include "common_bufsiz.h"
59
60typedef unsigned long long ullong;
61
62enum {
63 PROC_MIN_FILE_SIZE = 256,
64 PROC_MAX_FILE_SIZE = 16 * 1024,
65};
66
67typedef struct proc_file {
68 char *file;
69 int file_sz;
70 smallint last_gen;
71} proc_file;
72
73static const char *const proc_name[] ALIGN_PTR = {
74 "stat",
75 "loadavg",
76 "net/dev",
77 "meminfo",
78 "diskstats",
79 "sys/fs/file-nr"
80};
81
82struct globals {
83
84 smallint gen;
85
86 smallint is26;
87
88 smallint need_seconds;
89 char final_char;
90 char *cur_outbuf;
91 int delta;
92 unsigned deltanz;
93 struct timeval tv;
94 struct timeval start;
95#define first_proc_file proc_stat
96 proc_file proc_stat;
97 proc_file proc_loadavg;
98 proc_file proc_net_dev;
99 proc_file proc_meminfo;
100 proc_file proc_diskstats;
101 proc_file proc_sys_fs_filenr;
102};
103#define G (*ptr_to_globals)
104#define gen (G.gen )
105#define is26 (G.is26 )
106#define need_seconds (G.need_seconds )
107#define cur_outbuf (G.cur_outbuf )
108#define proc_stat (G.proc_stat )
109#define proc_loadavg (G.proc_loadavg )
110#define proc_net_dev (G.proc_net_dev )
111#define proc_meminfo (G.proc_meminfo )
112#define proc_diskstats (G.proc_diskstats )
113#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
114#define outbuf bb_common_bufsiz1
115#define INIT_G() do { \
116 setup_common_bufsiz(); \
117 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
118 cur_outbuf = outbuf; \
119 G.final_char = '\n'; \
120 G.deltanz = G.delta = 1000000; \
121} while (0)
122
123static inline void reset_outbuf(void)
124{
125 cur_outbuf = outbuf;
126}
127
128static void print_outbuf(void)
129{
130 int sz = cur_outbuf - outbuf;
131 if (sz > 0) {
132 xwrite(STDOUT_FILENO, outbuf, sz);
133 cur_outbuf = outbuf;
134 }
135}
136
137static void put(const char *s)
138{
139 char *p = cur_outbuf;
140 int sz = outbuf + COMMON_BUFSIZE - p;
141 while (*s && --sz >= 0)
142 *p++ = *s++;
143 cur_outbuf = p;
144}
145
146static void put_c(char c)
147{
148 if (cur_outbuf < outbuf + COMMON_BUFSIZE)
149 *cur_outbuf++ = c;
150}
151
152static void put_question_marks(int count)
153{
154 while (count--)
155 put_c('?');
156}
157
158static void readfile_z(proc_file *pf, const char* fname)
159{
160
161
162 int fd;
163 int sz, rdsz;
164 char *buf;
165
166 sz = pf->file_sz;
167 buf = pf->file;
168 if (!buf) {
169 buf = xmalloc(PROC_MIN_FILE_SIZE);
170 sz = PROC_MIN_FILE_SIZE;
171 }
172 again:
173 fd = xopen(fname, O_RDONLY);
174 buf[0] = '\0';
175 rdsz = read(fd, buf, sz-1);
176 close(fd);
177 if (rdsz > 0) {
178 if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) {
179 sz *= 2;
180 buf = xrealloc(buf, sz);
181 goto again;
182 }
183 buf[rdsz] = '\0';
184 }
185 pf->file_sz = sz;
186 pf->file = buf;
187}
188
189static const char* get_file(proc_file *pf)
190{
191 if (pf->last_gen != gen) {
192 pf->last_gen = gen;
193 readfile_z(pf, proc_name[pf - &first_proc_file]);
194 }
195 return pf->file;
196}
197
198static ullong read_after_slash(const char *p)
199{
200 p = strchr(p, '/');
201 if (!p) return 0;
202 return strtoull(p+1, NULL, 10);
203}
204
205enum conv_type {
206 conv_decimal = 0,
207 conv_slash = 1
208};
209
210
211
212
213
214
215
216static int rdval(const char* p, const char* key, ullong *vec, long posbits)
217{
218 unsigned curpos;
219
220 p = strstr(p, key);
221 if (!p) return 1;
222
223 p += strlen(key);
224 curpos = 1 << 1;
225 while (1) {
226 while (*p == ' ' || *p == '\t') p++;
227 if (*p == '\n' || *p == '\0') break;
228
229 if (curpos & posbits) {
230 *vec++ = (posbits & 1) == conv_decimal ?
231 strtoull(p, NULL, 10) :
232 read_after_slash(p);
233 posbits -= curpos;
234 if (posbits <= 1)
235 return 0;
236 }
237 while (*p > ' ')
238 p++;
239 curpos <<= 1;
240 }
241 return 0;
242}
243
244
245static int rdval_loadavg(const char* p, ullong *vec, long posbits)
246{
247 int result;
248 result = rdval(p, "", vec, posbits | conv_slash);
249 return result;
250}
251
252
253
254
255
256
257
258static int rdval_diskstats(const char* p, ullong *vec)
259{
260 char devname[32];
261 unsigned devname_len = 0;
262 int value_idx = 0;
263
264 vec[0] = 0;
265 vec[1] = 0;
266 while (1) {
267 value_idx++;
268 while (*p == ' ' || *p == '\t')
269 p++;
270 if (*p == '\0')
271 break;
272 if (*p == '\n') {
273 value_idx = 0;
274 p++;
275 continue;
276 }
277 if (value_idx == 3) {
278 char *end = strchrnul(p, ' ');
279
280 if (devname_len && strncmp(devname, p, devname_len) == 0 && isdigit(p[devname_len])) {
281 p = end;
282 goto skip_line;
283 }
284
285 devname_len = end - p;
286 if (devname_len > sizeof(devname)-1)
287 devname_len = sizeof(devname)-1;
288 strncpy(devname, p, devname_len);
289
290 p = end;
291 } else
292 if (value_idx == 6) {
293
294 vec[0] += strtoull(p, NULL, 10);
295 } else
296 if (value_idx == 10) {
297
298 vec[1] += strtoull(p, NULL, 10);
299 skip_line:
300 while (*p != '\n' && *p != '\0')
301 p++;
302 continue;
303 }
304 while ((unsigned char)(*p) > ' ')
305 p++;
306 }
307 return 0;
308}
309
310static void scale(ullong ul)
311{
312 char buf[5];
313
314
315 smart_ulltoa4(ul, buf, " kmgtpezy")[0] = '\0';
316 put(buf);
317}
318
319#define S_STAT(a) \
320typedef struct a { \
321 struct s_stat *next; \
322 void (*collect)(struct a *s) FAST_FUNC; \
323 const char *label;
324#define S_STAT_END(a) } a;
325
326S_STAT(s_stat)
327S_STAT_END(s_stat)
328
329static void FAST_FUNC collect_literal(s_stat *s UNUSED_PARAM)
330{
331}
332
333static s_stat* init_literal(void)
334{
335 s_stat *s = xzalloc(sizeof(*s));
336 s->collect = collect_literal;
337 return (s_stat*)s;
338}
339
340static s_stat* init_cr(const char *param UNUSED_PARAM)
341{
342 G.final_char = '\r';
343 return NULL;
344}
345
346
347
348
349enum { CPU_FIELDCNT = 7 };
350S_STAT(cpu_stat)
351 ullong old[CPU_FIELDCNT];
352 unsigned bar_sz;
353 char bar[1];
354S_STAT_END(cpu_stat)
355
356static void FAST_FUNC collect_cpu(cpu_stat *s)
357{
358 ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
359 unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
360 ullong all = 0;
361 unsigned norm_all = 0;
362 unsigned bar_sz = s->bar_sz;
363 char *bar = s->bar;
364 int i;
365
366 if (rdval(get_file(&proc_stat), "cpu ", data, 0
367 | (1 << 1)
368 | (1 << 2)
369 | (1 << 3)
370 | (1 << 4)
371 | (1 << 5)
372 | (1 << 6)
373 | (1 << 7))
374 ) {
375 put_question_marks(bar_sz);
376 return;
377 }
378
379 for (i = 0; i < CPU_FIELDCNT; i++) {
380 ullong old = s->old[i];
381 if (data[i] < old) old = data[i];
382 s->old[i] = data[i];
383 all += (data[i] -= old);
384 }
385
386 if (all) {
387 for (i = 0; i < CPU_FIELDCNT; i++) {
388 ullong t = bar_sz * data[i];
389 norm_all += data[i] = t / all;
390 frac[i] = t % all;
391 }
392
393 while (norm_all < bar_sz) {
394 unsigned max = frac[0];
395 int pos = 0;
396 for (i = 1; i < CPU_FIELDCNT; i++) {
397 if (frac[i] > max) max = frac[i], pos = i;
398 }
399 frac[pos] = 0;
400 data[pos]++;
401 norm_all++;
402 }
403
404 memset(bar, '.', bar_sz);
405 memset(bar, 'S', data[2]); bar += data[2];
406 memset(bar, 'U', data[0]); bar += data[0];
407 memset(bar, 'N', data[1]); bar += data[1];
408 memset(bar, 'D', data[4]); bar += data[4];
409 memset(bar, 'I', data[5]); bar += data[5];
410 memset(bar, 'i', data[6]); bar += data[6];
411 } else {
412 memset(bar, '?', bar_sz);
413 }
414 put(s->bar);
415}
416
417static s_stat* init_cpu(const char *param)
418{
419 int sz;
420 cpu_stat *s;
421 sz = param[0] ? strtoul(param, NULL, 0) : 10;
422 if (sz <= 0) sz = 1;
423 if (sz > 1000) sz = 1000;
424 s = xzalloc(sizeof(*s) + sz);
425
426 s->bar_sz = sz;
427 s->collect = collect_cpu;
428 return (s_stat*)s;
429}
430
431S_STAT(int_stat)
432 ullong old;
433 int no;
434S_STAT_END(int_stat)
435
436static void FAST_FUNC collect_int(int_stat *s)
437{
438 ullong data[1];
439 ullong old;
440
441 if (rdval(get_file(&proc_stat), "intr", data, 1 << s->no)) {
442 put_question_marks(4);
443 return;
444 }
445
446 old = s->old;
447 if (data[0] < old) old = data[0];
448 s->old = data[0];
449 scale(data[0] - old);
450}
451
452static s_stat* init_int(const char *param)
453{
454 int_stat *s = xzalloc(sizeof(*s));
455 s->collect = collect_int;
456 if (param[0] == '\0') {
457 s->no = 1;
458 } else {
459 int n = xatoi_positive(param);
460 s->no = n + 2;
461 }
462 return (s_stat*)s;
463}
464
465S_STAT(ctx_stat)
466 ullong old;
467S_STAT_END(ctx_stat)
468
469static void FAST_FUNC collect_ctx(ctx_stat *s)
470{
471 ullong data[1];
472 ullong old;
473
474 if (rdval(get_file(&proc_stat), "ctxt", data, 1 << 1)) {
475 put_question_marks(4);
476 return;
477 }
478
479 old = s->old;
480 if (data[0] < old) old = data[0];
481 s->old = data[0];
482 scale(data[0] - old);
483}
484
485static s_stat* init_ctx(const char *param UNUSED_PARAM)
486{
487 ctx_stat *s = xzalloc(sizeof(*s));
488 s->collect = collect_ctx;
489 return (s_stat*)s;
490}
491
492S_STAT(blk_stat)
493 const char* lookfor;
494 ullong old[2];
495S_STAT_END(blk_stat)
496
497static void FAST_FUNC collect_blk(blk_stat *s)
498{
499 ullong data[2];
500 int i;
501
502 if (is26) {
503 i = rdval_diskstats(get_file(&proc_diskstats), data);
504 } else {
505 i = rdval(get_file(&proc_stat), s->lookfor, data, 0
506 | (1 << 1)
507 | (1 << 2)
508 );
509
510 data[0] *= 2;
511 data[1] *= 2;
512 }
513 if (i) {
514 put_question_marks(9);
515 return;
516 }
517
518 for (i = 0; i < 2; i++) {
519 ullong old = s->old[i];
520 if (data[i] < old) old = data[i];
521 s->old[i] = data[i];
522 data[i] -= old;
523 }
524 scale(data[0]*512);
525 put_c(' ');
526 scale(data[1]*512);
527}
528
529static s_stat* init_blk(const char *param UNUSED_PARAM)
530{
531 blk_stat *s = xzalloc(sizeof(*s));
532 s->collect = collect_blk;
533 s->lookfor = "page";
534 return (s_stat*)s;
535}
536
537S_STAT(fork_stat)
538 ullong old;
539S_STAT_END(fork_stat)
540
541static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
542{
543 ullong data[1];
544
545 if (rdval_loadavg(get_file(&proc_loadavg), data, 1 << 4)) {
546 put_question_marks(4);
547 return;
548 }
549 scale(data[0]);
550}
551
552static void FAST_FUNC collect_fork(fork_stat *s)
553{
554 ullong data[1];
555 ullong old;
556
557 if (rdval(get_file(&proc_stat), "processes", data, 1 << 1)) {
558 put_question_marks(4);
559 return;
560 }
561
562 old = s->old;
563 if (data[0] < old) old = data[0];
564 s->old = data[0];
565 scale(data[0] - old);
566}
567
568static s_stat* init_fork(const char *param)
569{
570 fork_stat *s = xzalloc(sizeof(*s));
571 if (*param == 'n') {
572 s->collect = collect_thread_nr;
573 } else {
574 s->collect = collect_fork;
575 }
576 return (s_stat*)s;
577}
578
579S_STAT(if_stat)
580 ullong old[4];
581 const char *device;
582 char *device_colon;
583S_STAT_END(if_stat)
584
585static void FAST_FUNC collect_if(if_stat *s)
586{
587 ullong data[4];
588 int i;
589
590 if (rdval(get_file(&proc_net_dev), s->device_colon, data, 0
591 | (1 << 1)
592 | (1 << 3)
593 | (1 << 9)
594 | (1 << 11))
595 ) {
596 put_question_marks(10);
597 return;
598 }
599
600 for (i = 0; i < 4; i++) {
601 ullong old = s->old[i];
602 if (data[i] < old) old = data[i];
603 s->old[i] = data[i];
604 data[i] -= old;
605 }
606 put_c(data[1] ? '*' : ' ');
607 scale(data[0]);
608 put_c(data[3] ? '*' : ' ');
609 scale(data[2]);
610}
611
612static s_stat* init_if(const char *device)
613{
614 if_stat *s = xzalloc(sizeof(*s));
615
616 if (!device || !device[0])
617 bb_show_usage();
618 s->collect = collect_if;
619
620 s->device = device;
621 s->device_colon = xasprintf("%s:", device);
622 return (s_stat*)s;
623}
624
625S_STAT(mem_stat)
626 char opt;
627S_STAT_END(mem_stat)
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664static void FAST_FUNC collect_mem(mem_stat *s)
665{
666 ullong m_total;
667 ullong m_free;
668 ullong m_bufs;
669 ullong m_cached;
670 ullong m_slab;
671
672 const char *meminfo = get_file(&proc_meminfo);
673
674 if (s->opt == 'd'
675 || s->opt == 'w'
676 ) {
677 m_total = 0;
678 if (rdval(meminfo,
679 (s->opt == 'd' ? "Dirty:" : "Writeback:"),
680 &m_total, 1 << 1)
681 ) {
682 put_question_marks(4);
683 return;
684 }
685 scale(m_total << 10);
686 return;
687 }
688
689 m_total = 0;
690 if (rdval(meminfo, "MemTotal:", &m_total, 1 << 1)) {
691 put_question_marks(4);
692 return;
693 }
694 if (s->opt == 't') {
695 scale(m_total << 10);
696 return;
697 }
698
699 m_free = 0;
700 m_bufs = 0;
701 m_cached = 0;
702 m_slab = 0;
703 if (rdval(meminfo, "MemFree:", &m_free , 1 << 1)
704 || rdval(meminfo, "Buffers:", &m_bufs , 1 << 1)
705 || rdval(meminfo, "Cached:", &m_cached, 1 << 1)
706 || rdval(meminfo, "Slab:", &m_slab , 1 << 1)
707 ) {
708 put_question_marks(4);
709 return;
710 }
711
712 m_free += m_bufs + m_cached + m_slab;
713 switch (s->opt) {
714 case 'f':
715 scale(m_free << 10); break;
716 default:
717 scale((m_total - m_free) << 10); break;
718 }
719}
720
721static s_stat* init_mem(const char *param)
722{
723 mem_stat *s = xzalloc(sizeof(*s));
724 s->collect = collect_mem;
725 s->opt = param[0];
726 return (s_stat*)s;
727}
728
729S_STAT(swp_stat)
730S_STAT_END(swp_stat)
731
732static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
733{
734 ullong s_total[1];
735 ullong s_free[1];
736 if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1 << 1)
737 || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1 << 1)
738 ) {
739 put_question_marks(4);
740 return;
741 }
742 scale((s_total[0]-s_free[0]) << 10);
743}
744
745static s_stat* init_swp(const char *param UNUSED_PARAM)
746{
747 swp_stat *s = xzalloc(sizeof(*s));
748 s->collect = collect_swp;
749 return (s_stat*)s;
750}
751
752S_STAT(fd_stat)
753S_STAT_END(fd_stat)
754
755static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
756{
757 ullong data[2];
758
759 if (rdval(get_file(&proc_sys_fs_filenr), "", data, 0
760 | (1 << 1)
761 | (1 << 2))
762 ) {
763 put_question_marks(4);
764 return;
765 }
766
767 scale(data[0] - data[1]);
768}
769
770static s_stat* init_fd(const char *param UNUSED_PARAM)
771{
772 fd_stat *s = xzalloc(sizeof(*s));
773 s->collect = collect_fd;
774 return (s_stat*)s;
775}
776
777S_STAT(time_stat)
778 unsigned prec;
779 unsigned scale;
780S_STAT_END(time_stat)
781
782static void FAST_FUNC collect_tv(time_stat *s, struct timeval *tv, int local)
783{
784 char buf[sizeof("12:34:56.123456")];
785 struct tm* tm;
786 unsigned us = tv->tv_usec + s->scale/2;
787 time_t t = tv->tv_sec;
788
789 if (us >= 1000000) {
790 t++;
791 us -= 1000000;
792 }
793 if (local)
794 tm = localtime(&t);
795 else
796 tm = gmtime(&t);
797
798 sprintf(buf, "%02u:%02u:%02u", tm->tm_hour, tm->tm_min, tm->tm_sec);
799 if (s->prec)
800 sprintf(buf+8, ".%0*u", s->prec, us / s->scale);
801 put(buf);
802}
803
804static void FAST_FUNC collect_time(time_stat *s)
805{
806 collect_tv(s, &G.tv, 1);
807}
808
809static void FAST_FUNC collect_monotonic(time_stat *s)
810{
811 struct timeval tv_mono;
812
813 tv_mono.tv_sec = G.tv.tv_sec - G.start.tv_sec;
814#if 0
815 if (tv_mono.tv_sec < 0) {
816
817 tv_mono.tv_sec = 0;
818 G.start = G.tv;
819 }
820#endif
821 tv_mono.tv_usec = G.tv.tv_usec - G.start.tv_usec;
822 if ((int32_t)tv_mono.tv_usec < 0) {
823 tv_mono.tv_usec += 1000000;
824 tv_mono.tv_sec--;
825 }
826 collect_tv(s, &tv_mono, 0);
827}
828
829static s_stat* init_time(const char *param)
830{
831 int prec;
832 time_stat *s = xzalloc(sizeof(*s));
833
834 s->collect = collect_time;
835 prec = param[0] - '0';
836 if (prec < 0) prec = 0;
837 else if (prec > 6) prec = 6;
838 s->prec = prec;
839 s->scale = 1;
840 while (prec++ < 6)
841 s->scale *= 10;
842 return (s_stat*)s;
843}
844
845static s_stat* init_monotonic(const char *param)
846{
847 time_stat *s = (void*)init_time(param);
848 s->collect = collect_monotonic;
849 return (s_stat*)s;
850}
851
852static void FAST_FUNC collect_info(s_stat *s)
853{
854 gen ^= 1;
855 while (s) {
856 put(s->label);
857 s->collect(s);
858 s = s->next;
859 }
860}
861
862typedef s_stat* init_func(const char *param);
863
864static const char options[] ALIGN1 = "ncmsfixptTbr";
865static init_func *const init_functions[] ALIGN_PTR = {
866 init_if,
867 init_cpu,
868 init_mem,
869 init_swp,
870 init_fd,
871 init_int,
872 init_ctx,
873 init_fork,
874 init_time,
875 init_monotonic,
876 init_blk,
877 init_cr
878};
879
880int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
881int nmeter_main(int argc UNUSED_PARAM, char **argv)
882{
883 char buf[32];
884 s_stat *first = NULL;
885 s_stat *last = NULL;
886 s_stat *s;
887 char *opt_d;
888 char *cur, *prev;
889
890 INIT_G();
891
892 xchdir("/proc");
893
894 if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
895 buf[sizeof(buf)-1] = '\0';
896 is26 = (strstr(buf, " 2.4.") == NULL);
897 }
898
899 if (getopt32(argv, "d:", &opt_d)) {
900 G.delta = xatoi(opt_d) * 1000;
901 G.deltanz = G.delta > 0 ? G.delta : 1;
902 need_seconds = (1000000 % G.deltanz) != 0;
903 }
904 argv += optind;
905
906 if (!argv[0])
907 bb_show_usage();
908
909
910
911 cur = xstrdup(argv[0]);
912 while (1) {
913 char *param, *p;
914 prev = cur;
915 again:
916 cur = strchr(cur, '%');
917 if (!cur)
918 break;
919 if (cur[1] == '%') {
920 overlapping_strcpy(cur, cur + 1);
921 cur++;
922 goto again;
923 }
924 *cur++ = '\0';
925 if (cur[0] == '[') {
926
927 cur++;
928 p = strchr(options, cur[0]);
929 param = cur+1;
930 while (cur[0] != ']') {
931 if (!cur[0])
932 bb_show_usage();
933 cur++;
934 }
935 *cur++ = '\0';
936 } else {
937
938 param = cur;
939 while (cur[0] >= '0' && cur[0] <= '9')
940 cur++;
941 if (!cur[0])
942 bb_show_usage();
943 p = strchr(options, cur[0]);
944 *cur++ = '\0';
945 }
946 if (!p)
947 bb_show_usage();
948 s = init_functions[p-options](param);
949 if (s) {
950 s->label = prev;
951
952 if (!first)
953 first = s;
954 else
955 last->next = s;
956 last = s;
957 } else {
958
959 overlapping_strcpy(prev + strlen(prev), cur);
960 cur = prev;
961 }
962 }
963 if (prev[0]) {
964 s = init_literal();
965 s->label = prev;
966
967 if (!first)
968 first = s;
969 else
970 last->next = s;
971 last = s;
972 }
973
974
975 collect_info(first);
976 reset_outbuf();
977
978 if (G.delta >= 0) {
979 xgettimeofday(&G.tv);
980 usleep(G.delta > 1000000 ? 1000000 : G.delta - G.tv.tv_usec % G.deltanz);
981 }
982
983 xgettimeofday(&G.start);
984 G.tv = G.start;
985 while (1) {
986 collect_info(first);
987 put_c(G.final_char);
988 print_outbuf();
989
990
991
992
993
994
995 if (G.delta >= 0) {
996 int rem;
997
998 xgettimeofday(&G.tv);
999 if (need_seconds)
1000 rem = G.delta - ((ullong)G.tv.tv_sec*1000000 + G.tv.tv_usec) % G.deltanz;
1001 else
1002 rem = G.delta - (unsigned)G.tv.tv_usec % G.deltanz;
1003
1004
1005 if (rem < (unsigned)G.delta / 128) {
1006 rem += G.delta;
1007 }
1008 usleep(rem);
1009 }
1010 xgettimeofday(&G.tv);
1011 }
1012
1013
1014}
1015