]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devcons.c
merge
[plan9front.git] / sys / src / 9 / port / devcons.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7 #include        "pool.h"
8
9 #include        <authsrv.h>
10
11 void    (*consdebug)(void) = nil;
12 void    (*screenputs)(char*, int) = nil;
13
14 Queue*  serialoq;               /* serial console output */
15 Queue*  kprintoq;               /* console output, for /dev/kprint */
16 ulong   kprintinuse;            /* test and set whether /dev/kprint is open */
17 int     iprintscreenputs = 1;
18
19 int     panicking;
20
21 char    *sysname;
22 vlong   fasthz;
23
24 static void     seedrand(void);
25 static int      readtime(ulong, char*, int);
26 static int      readbintime(char*, int);
27 static int      writetime(char*, int);
28 static int      writebintime(char*, int);
29
30 enum
31 {
32         CMhalt,
33         CMreboot,
34         CMpanic,
35         CMrdb,
36 };
37
38 Cmdtab rebootmsg[] =
39 {
40         CMhalt,         "halt",         1,
41         CMreboot,       "reboot",       0,
42         CMpanic,        "panic",        0,
43         CMrdb,          "rdb",          0,
44 };
45
46 void
47 printinit(void)
48 {
49 }
50
51 int
52 consactive(void)
53 {
54         if(serialoq)
55                 return qlen(serialoq) > 0;
56         return 0;
57 }
58
59 void
60 prflush(void)
61 {
62         ulong now;
63
64         now = m->ticks;
65         while(consactive())
66                 if(m->ticks - now >= HZ)
67                         break;
68 }
69
70 /*
71  * Log console output so it can be retrieved via /dev/kmesg.
72  * This is good for catching boot-time messages after the fact.
73  */
74 struct {
75         Lock lk;
76         char buf[16384];
77         uint n;
78 } kmesg;
79
80 static void
81 kmesgputs(char *str, int n)
82 {
83         uint nn, d;
84
85         ilock(&kmesg.lk);
86         /* take the tail of huge writes */
87         if(n > sizeof kmesg.buf){
88                 d = n - sizeof kmesg.buf;
89                 str += d;
90                 n -= d;
91         }
92
93         /* slide the buffer down to make room */
94         nn = kmesg.n;
95         if(nn + n >= sizeof kmesg.buf){
96                 d = nn + n - sizeof kmesg.buf;
97                 if(d)
98                         memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d);
99                 nn -= d;
100         }
101
102         /* copy the data in */
103         memmove(kmesg.buf+nn, str, n);
104         nn += n;
105         kmesg.n = nn;
106         iunlock(&kmesg.lk);
107 }
108
109 /*
110  *   Print a string on the console.  Convert \n to \r\n for serial
111  *   line consoles.  Locking of the queues is left up to the screen
112  *   or uart code.  Multi-line messages to serial consoles may get
113  *   interspersed with other messages.
114  */
115 static void
116 putstrn0(char *str, int n, int usewrite)
117 {
118         int m;
119         char *t;
120
121         if(!islo())
122                 usewrite = 0;
123
124         /*
125          *  how many different output devices do we need?
126          */
127         kmesgputs(str, n);
128
129         /*
130          *  if someone is reading /dev/kprint,
131          *  put the message there.
132          *  if not and there's an attached bit mapped display,
133          *  put the message there.
134          *
135          *  if there's a serial line being used as a console,
136          *  put the message there.
137          */
138         if(kprintoq != nil && !qisclosed(kprintoq)){
139                 if(usewrite)
140                         qwrite(kprintoq, str, n);
141                 else
142                         qiwrite(kprintoq, str, n);
143         }else if(screenputs != nil)
144                 screenputs(str, n);
145
146         if(serialoq == nil){
147                 uartputs(str, n);
148                 return;
149         }
150
151         while(n > 0) {
152                 t = memchr(str, '\n', n);
153                 if(t) {
154                         m = t-str;
155                         if(usewrite){
156                                 qwrite(serialoq, str, m);
157                                 qwrite(serialoq, "\r\n", 2);
158                         } else {
159                                 qiwrite(serialoq, str, m);
160                                 qiwrite(serialoq, "\r\n", 2);
161                         }
162                         n -= m+1;
163                         str = t+1;
164                 } else {
165                         if(usewrite)
166                                 qwrite(serialoq, str, n);
167                         else
168                                 qiwrite(serialoq, str, n);
169                         break;
170                 }
171         }
172 }
173
174 void
175 putstrn(char *str, int n)
176 {
177         putstrn0(str, n, 0);
178 }
179
180 int
181 print(char *fmt, ...)
182 {
183         int n;
184         va_list arg;
185         char buf[PRINTSIZE];
186
187         va_start(arg, fmt);
188         n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
189         va_end(arg);
190         putstrn(buf, n);
191
192         return n;
193 }
194
195 /*
196  * Want to interlock iprints to avoid interlaced output on 
197  * multiprocessor, but don't want to deadlock if one processor
198  * dies during print and another has something important to say.
199  * Make a good faith effort.
200  */
201 static Lock iprintlock;
202 static int
203 iprintcanlock(Lock *l)
204 {
205         int i;
206         
207         for(i=0; i<1000; i++){
208                 if(canlock(l))
209                         return 1;
210                 if(l->m == MACHP(m->machno))
211                         return 0;
212                 microdelay(100);
213         }
214         return 0;
215 }
216
217 int
218 iprint(char *fmt, ...)
219 {
220         int n, s, locked;
221         va_list arg;
222         char buf[PRINTSIZE];
223
224         s = splhi();
225         va_start(arg, fmt);
226         n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
227         va_end(arg);
228         locked = iprintcanlock(&iprintlock);
229         if(screenputs != nil && iprintscreenputs)
230                 screenputs(buf, n);
231         uartputs(buf, n);
232         if(locked)
233                 unlock(&iprintlock);
234         splx(s);
235
236         return n;
237 }
238
239 void
240 panic(char *fmt, ...)
241 {
242         int s;
243         va_list arg;
244         char buf[PRINTSIZE];
245
246         kprintoq = nil; /* don't try to write to /dev/kprint */
247
248         if(panicking)
249                 for(;;);
250         panicking = 1;
251
252         s = splhi();
253         strcpy(buf, "panic: ");
254         va_start(arg, fmt);
255         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
256         va_end(arg);
257         iprint("%s\n", buf);
258         if(consdebug)
259                 (*consdebug)();
260         splx(s);
261         prflush();
262         dumpstack();
263         if(!cpuserver)
264                 for(;;);
265         exit(1);
266 }
267
268 /* libmp at least contains a few calls to sysfatal; simulate with panic */
269 void
270 sysfatal(char *fmt, ...)
271 {
272         char err[256];
273         va_list arg;
274
275         va_start(arg, fmt);
276         vseprint(err, err + sizeof err, fmt, arg);
277         va_end(arg);
278         panic("sysfatal: %s", err);
279 }
280
281 void
282 _assert(char *fmt)
283 {
284         panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt);
285 }
286
287 int
288 pprint(char *fmt, ...)
289 {
290         int n;
291         Chan *c;
292         va_list arg;
293         char buf[2*PRINTSIZE];
294
295         if(up == nil || up->fgrp == nil)
296                 return 0;
297
298         c = up->fgrp->fd[2];
299         if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR))
300                 return 0;
301         n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid);
302         va_start(arg, fmt);
303         n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
304         va_end(arg);
305
306         if(waserror())
307                 return 0;
308         devtab[c->type]->write(c, buf, n, c->offset);
309         poperror();
310
311         lock(c);
312         c->offset += n;
313         unlock(c);
314
315         return n;
316 }
317
318 enum{
319         Qdir,
320         Qbintime,
321         Qcons,
322         Qconsctl,
323         Qcputime,
324         Qdrivers,
325         Qkmesg,
326         Qkprint,
327         Qhostdomain,
328         Qhostowner,
329         Qnull,
330         Qosversion,
331         Qpgrpid,
332         Qpid,
333         Qppid,
334         Qrandom,
335         Qreboot,
336         Qswap,
337         Qsysname,
338         Qsysstat,
339         Qtime,
340         Quser,
341         Qzero,
342         Qmordor,
343         Qconfig,
344 };
345
346 enum
347 {
348         VLNUMSIZE=      22,
349 };
350
351 static Dirtab consdir[]={
352         ".",    {Qdir, 0, QTDIR},       0,              DMDIR|0555,
353         "bintime",      {Qbintime},     24,             0664,
354         "cons",         {Qcons},        0,              0660,
355         "consctl",      {Qconsctl},     0,              0220,
356         "cputime",      {Qcputime},     6*NUMSIZE,      0444,
357         "drivers",      {Qdrivers},     0,              0444,
358         "hostdomain",   {Qhostdomain},  DOMLEN,         0664,
359         "hostowner",    {Qhostowner},   0,              0664,
360         "kmesg",        {Qkmesg},       0,              0440,
361         "kprint",       {Qkprint, 0, QTEXCL},   0,      DMEXCL|0440,
362         "null",         {Qnull},        0,              0666,
363         "osversion",    {Qosversion},   0,              0444,
364         "pgrpid",       {Qpgrpid},      NUMSIZE,        0444,
365         "pid",          {Qpid},         NUMSIZE,        0444,
366         "ppid",         {Qppid},        NUMSIZE,        0444,
367         "random",       {Qrandom},      0,              0444,
368         "reboot",       {Qreboot},      0,              0664,
369         "swap",         {Qswap},        0,              0664,
370         "sysname",      {Qsysname},     0,              0664,
371         "sysstat",      {Qsysstat},     0,              0666,
372         "time",         {Qtime},        NUMSIZE+3*VLNUMSIZE,    0664,
373         "user",         {Quser},        0,              0666,
374         "zero",         {Qzero},        0,              0444,
375         "config",       {Qconfig},      0,              0444,
376         "mordor",       {Qmordor},      0,              0666,
377 };
378
379 int
380 readnum(ulong off, char *buf, ulong n, ulong val, int size)
381 {
382         char tmp[64];
383
384         snprint(tmp, sizeof(tmp), "%*lud", size-1, val);
385         tmp[size-1] = ' ';
386         if(off >= size)
387                 return 0;
388         if(off+n > size)
389                 n = size-off;
390         memmove(buf, tmp+off, n);
391         return n;
392 }
393
394 int
395 readstr(ulong off, char *buf, ulong n, char *str)
396 {
397         int size;
398
399         size = strlen(str);
400         if(off >= size)
401                 return 0;
402         if(off+n > size)
403                 n = size-off;
404         memmove(buf, str+off, n);
405         return n;
406 }
407
408 static void
409 consinit(void)
410 {
411         todinit();
412         randominit();
413 }
414
415 static Chan*
416 consattach(char *spec)
417 {
418         return devattach('c', spec);
419 }
420
421 static Walkqid*
422 conswalk(Chan *c, Chan *nc, char **name, int nname)
423 {
424         return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
425 }
426
427 static int
428 consstat(Chan *c, uchar *dp, int n)
429 {
430         return devstat(c, dp, n, consdir, nelem(consdir), devgen);
431 }
432
433 static Chan*
434 consopen(Chan *c, int omode)
435 {
436         c->aux = nil;
437         c = devopen(c, omode, consdir, nelem(consdir), devgen);
438         switch((ulong)c->qid.path){
439         case Qkprint:
440                 if(tas(&kprintinuse) != 0){
441                         c->flag &= ~COPEN;
442                         error(Einuse);
443                 }
444                 if(kprintoq == nil){
445                         kprintoq = qopen(8*1024, Qcoalesce, 0, 0);
446                         if(kprintoq == nil){
447                                 c->flag &= ~COPEN;
448                                 error(Enomem);
449                         }
450                         qnoblock(kprintoq, 1);
451                 }else
452                         qreopen(kprintoq);
453                 c->iounit = qiomaxatomic;
454                 break;
455         }
456         return c;
457 }
458
459 static void
460 consclose(Chan *c)
461 {
462         switch((ulong)c->qid.path){
463         /* close of kprint allows other opens */
464         case Qkprint:
465                 if(c->flag & COPEN){
466                         kprintinuse = 0;
467                         qhangup(kprintoq, nil);
468                 }
469                 break;
470         }
471 }
472
473 static long
474 consread(Chan *c, void *buf, long n, vlong off)
475 {
476         ulong l;
477         Mach *mp;
478         char *b, *bp;
479         char tmp[256];          /* must be >= 18*NUMSIZE (Qswap) */
480         int i, k, id;
481         vlong offset = off;
482         extern char configfile[];
483
484         if(n <= 0)
485                 return n;
486
487         switch((ulong)c->qid.path){
488         case Qdir:
489                 return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
490
491         case Qcons:
492                 error(Egreg);
493
494         case Qcputime:
495                 k = offset;
496                 if(k >= 6*NUMSIZE)
497                         return 0;
498                 if(k+n > 6*NUMSIZE)
499                         n = 6*NUMSIZE - k;
500                 /* easiest to format in a separate buffer and copy out */
501                 for(i=0; i<6 && NUMSIZE*i<k+n; i++){
502                         l = up->time[i];
503                         if(i == TReal)
504                                 l = MACHP(0)->ticks - l;
505                         l = TK2MS(l);
506                         readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
507                 }
508                 memmove(buf, tmp+k, n);
509                 return n;
510
511         case Qkmesg:
512                 /*
513                  * This is unlocked to avoid tying up a process
514                  * that's writing to the buffer.  kmesg.n never 
515                  * gets smaller, so worst case the reader will
516                  * see a slurred buffer.
517                  */
518                 if(off >= kmesg.n)
519                         n = 0;
520                 else{
521                         if(off+n > kmesg.n)
522                                 n = kmesg.n - off;
523                         memmove(buf, kmesg.buf+off, n);
524                 }
525                 return n;
526                 
527         case Qkprint:
528                 return qread(kprintoq, buf, n);
529
530         case Qpgrpid:
531                 return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE);
532
533         case Qpid:
534                 return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
535
536         case Qppid:
537                 return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
538
539         case Qtime:
540                 return readtime((ulong)offset, buf, n);
541
542         case Qbintime:
543                 return readbintime(buf, n);
544
545         case Qhostowner:
546                 return readstr((ulong)offset, buf, n, eve);
547
548         case Qhostdomain:
549                 return readstr((ulong)offset, buf, n, hostdomain);
550
551         case Quser:
552                 return readstr((ulong)offset, buf, n, up->user);
553
554         case Qnull:
555                 return 0;
556
557         case Qconfig:
558                 return readstr((ulong)offset, buf, n, configfile);
559
560         case Qsysstat:
561                 b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1);     /* +1 for NUL */
562                 bp = b;
563                 for(id = 0; id < 32; id++) {
564                         if(active.machs & (1<<id)) {
565                                 mp = MACHP(id);
566                                 readnum(0, bp, NUMSIZE, id, NUMSIZE);
567                                 bp += NUMSIZE;
568                                 readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
569                                 bp += NUMSIZE;
570                                 readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
571                                 bp += NUMSIZE;
572                                 readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
573                                 bp += NUMSIZE;
574                                 readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
575                                 bp += NUMSIZE;
576                                 readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
577                                 bp += NUMSIZE;
578                                 readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
579                                 bp += NUMSIZE;
580                                 readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
581                                 bp += NUMSIZE;
582                                 readnum(0, bp, NUMSIZE,
583                                         (mp->perf.avg_inidle*100)/mp->perf.period,
584                                         NUMSIZE);
585                                 bp += NUMSIZE;
586                                 readnum(0, bp, NUMSIZE,
587                                         (mp->perf.avg_inintr*100)/mp->perf.period,
588                                         NUMSIZE);
589                                 bp += NUMSIZE;
590                                 *bp++ = '\n';
591                         }
592                 }
593                 if(waserror()){
594                         free(b);
595                         nexterror();
596                 }
597                 n = readstr((ulong)offset, buf, n, b);
598                 free(b);
599                 poperror();
600                 return n;
601
602         case Qswap:
603                 snprint(tmp, sizeof tmp,
604                         "%lud memory\n"
605                         "%d pagesize\n"
606                         "%lud kernel\n"
607                         "%lud/%lud user\n"
608                         "%lud/%lud swap\n"
609                         "%lud/%lud kernel malloc\n"
610                         "%lud/%lud kernel draw\n",
611                         conf.npage*BY2PG,
612                         BY2PG,
613                         conf.npage-conf.upages,
614                         palloc.user-palloc.freecount, palloc.user,
615                         conf.nswap-swapalloc.free, conf.nswap,
616                         mainmem->cursize, mainmem->maxsize,
617                         imagmem->cursize, imagmem->maxsize);
618
619                 return readstr((ulong)offset, buf, n, tmp);
620
621         case Qsysname:
622                 if(sysname == nil)
623                         return 0;
624                 return readstr((ulong)offset, buf, n, sysname);
625
626         case Qrandom:
627                 return randomread(buf, n);
628
629         case Qdrivers:
630                 b = smalloc(READSTR);
631                 k = 0;
632                 for(i = 0; devtab[i] != nil; i++)
633                         k += snprint(b+k, READSTR-k, "#%C %s\n",
634                                 devtab[i]->dc, devtab[i]->name);
635                 if(waserror()){
636                         free(b);
637                         nexterror();
638                 }
639                 n = readstr((ulong)offset, buf, n, b);
640                 poperror();
641                 free(b);
642                 return n;
643
644         case Qzero:
645                 memset(buf, 0, n);
646                 return n;
647         
648         case Qmordor:
649                 error("one does not simply read from mordor");
650                 return 0;
651
652         case Qosversion:
653                 snprint(tmp, sizeof tmp, "2000");
654                 n = readstr((ulong)offset, buf, n, tmp);
655                 return n;
656
657         default:
658                 print("consread %#llux\n", c->qid.path);
659                 error(Egreg);
660         }
661         return -1;              /* never reached */
662 }
663
664 static long
665 conswrite(Chan *c, void *va, long n, vlong off)
666 {
667         char buf[256];
668         long l, bp;
669         char *a;
670         Mach *mp;
671         int id, fd;
672         Chan *swc;
673         ulong offset;
674         Cmdbuf *cb;
675         Cmdtab *ct;
676
677         a = va;
678         offset = off;
679
680         switch((ulong)c->qid.path){
681         case Qcons:
682                 /*
683                  * Can't page fault in putstrn, so copy the data locally.
684                  */
685                 l = n;
686                 while(l > 0){
687                         bp = l;
688                         if(bp > sizeof buf)
689                                 bp = sizeof buf;
690                         memmove(buf, a, bp);
691                         putstrn0(buf, bp, 1);
692                         a += bp;
693                         l -= bp;
694                 }
695                 break;
696
697         case Qconsctl:
698                 error(Egreg);
699
700         case Qtime:
701                 if(!iseve())
702                         error(Eperm);
703                 return writetime(a, n);
704
705         case Qbintime:
706                 if(!iseve())
707                         error(Eperm);
708                 return writebintime(a, n);
709
710         case Qhostowner:
711                 return hostownerwrite(a, n);
712
713         case Qhostdomain:
714                 return hostdomainwrite(a, n);
715
716         case Quser:
717                 return userwrite(a, n);
718
719         case Qnull:
720                 break;
721
722         case Qconfig:
723                 error(Eperm);
724                 break;
725
726         case Qreboot:
727                 if(!iseve())
728                         error(Eperm);
729                 cb = parsecmd(a, n);
730
731                 if(waserror()) {
732                         free(cb);
733                         nexterror();
734                 }
735                 ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
736                 switch(ct->index) {
737                 case CMhalt:
738                         reboot(nil, 0, 0);
739                         break;
740                 case CMreboot:
741                         rebootcmd(cb->nf-1, cb->f+1);
742                         break;
743                 case CMpanic:
744                         *(ulong*)0=0;
745                         panic("/dev/reboot");
746                 case CMrdb:
747                         if(consdebug == nil)
748                                 consdebug = rdb;
749                         consdebug();
750                         break;
751                 }
752                 poperror();
753                 free(cb);
754                 break;
755
756         case Qsysstat:
757                 for(id = 0; id < 32; id++) {
758                         if(active.machs & (1<<id)) {
759                                 mp = MACHP(id);
760                                 mp->cs = 0;
761                                 mp->intr = 0;
762                                 mp->syscall = 0;
763                                 mp->pfault = 0;
764                                 mp->tlbfault = 0;
765                                 mp->tlbpurge = 0;
766                         }
767                 }
768                 break;
769
770         case Qswap:
771                 if(n >= sizeof buf)
772                         error(Egreg);
773                 memmove(buf, va, n);    /* so we can NUL-terminate */
774                 buf[n] = 0;
775                 /* start a pager if not already started */
776                 if(strncmp(buf, "start", 5) == 0){
777                         kickpager();
778                         break;
779                 }
780                 if(!iseve())
781                         error(Eperm);
782                 if(buf[0]<'0' || '9'<buf[0])
783                         error(Ebadarg);
784                 fd = strtoul(buf, 0, 0);
785                 swc = fdtochan(fd, -1, 1, 1);
786                 setswapchan(swc);
787                 break;
788
789         case Qsysname:
790                 if(offset != 0)
791                         error(Ebadarg);
792                 if(n <= 0 || n >= sizeof buf)
793                         error(Ebadarg);
794                 strncpy(buf, a, n);
795                 buf[n] = 0;
796                 if(buf[n-1] == '\n')
797                         buf[n-1] = 0;
798                 kstrdup(&sysname, buf);
799                 break;
800         
801         case Qmordor:
802                 error("one does not simply write into mordor");
803                 return 0;
804
805         default:
806                 print("conswrite: %#llux\n", c->qid.path);
807                 error(Egreg);
808         }
809         return n;
810 }
811
812 Dev consdevtab = {
813         'c',
814         "cons",
815
816         devreset,
817         consinit,
818         devshutdown,
819         consattach,
820         conswalk,
821         consstat,
822         consopen,
823         devcreate,
824         consclose,
825         consread,
826         devbread,
827         conswrite,
828         devbwrite,
829         devremove,
830         devwstat,
831 };
832
833 static  ulong   randn;
834
835 static void
836 seedrand(void)
837 {
838         if(!waserror()){
839                 randomread((void*)&randn, sizeof(randn));
840                 poperror();
841         }
842 }
843
844 int
845 nrand(int n)
846 {
847         if(randn == 0)
848                 seedrand();
849         randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
850         return (randn>>16) % n;
851 }
852
853 int
854 rand(void)
855 {
856         nrand(1);
857         return randn;
858 }
859
860 static uvlong uvorder = 0x0001020304050607ULL;
861
862 static uchar*
863 le2vlong(vlong *to, uchar *f)
864 {
865         uchar *t, *o;
866         int i;
867
868         t = (uchar*)to;
869         o = (uchar*)&uvorder;
870         for(i = 0; i < sizeof(vlong); i++)
871                 t[o[i]] = f[i];
872         return f+sizeof(vlong);
873 }
874
875 static uchar*
876 vlong2le(uchar *t, vlong from)
877 {
878         uchar *f, *o;
879         int i;
880
881         f = (uchar*)&from;
882         o = (uchar*)&uvorder;
883         for(i = 0; i < sizeof(vlong); i++)
884                 t[i] = f[o[i]];
885         return t+sizeof(vlong);
886 }
887
888 static long order = 0x00010203;
889
890 static uchar*
891 le2long(long *to, uchar *f)
892 {
893         uchar *t, *o;
894         int i;
895
896         t = (uchar*)to;
897         o = (uchar*)&order;
898         for(i = 0; i < sizeof(long); i++)
899                 t[o[i]] = f[i];
900         return f+sizeof(long);
901 }
902
903 static uchar*
904 long2le(uchar *t, long from)
905 {
906         uchar *f, *o;
907         int i;
908
909         f = (uchar*)&from;
910         o = (uchar*)&order;
911         for(i = 0; i < sizeof(long); i++)
912                 t[i] = f[o[i]];
913         return t+sizeof(long);
914 }
915
916 char *Ebadtimectl = "bad time control";
917
918 /*
919  *  like the old #c/time but with added info.  Return
920  *
921  *      secs    nanosecs        fastticks       fasthz
922  */
923 static int
924 readtime(ulong off, char *buf, int n)
925 {
926         vlong   nsec, ticks;
927         long sec;
928         char str[7*NUMSIZE];
929
930         nsec = todget(&ticks);
931         if(fasthz == 0LL)
932                 fastticks((uvlong*)&fasthz);
933         sec = nsec/1000000000ULL;
934         snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
935                 NUMSIZE-1, sec,
936                 VLNUMSIZE-1, nsec,
937                 VLNUMSIZE-1, ticks,
938                 VLNUMSIZE-1, fasthz);
939         return readstr(off, buf, n, str);
940 }
941
942 /*
943  *  set the time in seconds
944  */
945 static int
946 writetime(char *buf, int n)
947 {
948         char b[13];
949         long i;
950         vlong now;
951
952         if(n >= sizeof(b))
953                 error(Ebadtimectl);
954         strncpy(b, buf, n);
955         b[n] = 0;
956         i = strtol(b, 0, 0);
957         if(i <= 0)
958                 error(Ebadtimectl);
959         now = i*1000000000LL;
960         todset(now, 0, 0);
961         return n;
962 }
963
964 /*
965  *  read binary time info.  all numbers are little endian.
966  *  ticks and nsec are syncronized.
967  */
968 static int
969 readbintime(char *buf, int n)
970 {
971         int i;
972         vlong nsec, ticks;
973         uchar *b = (uchar*)buf;
974
975         i = 0;
976         if(fasthz == 0LL)
977                 fastticks((uvlong*)&fasthz);
978         nsec = todget(&ticks);
979         if(n >= 3*sizeof(uvlong)){
980                 vlong2le(b+2*sizeof(uvlong), fasthz);
981                 i += sizeof(uvlong);
982         }
983         if(n >= 2*sizeof(uvlong)){
984                 vlong2le(b+sizeof(uvlong), ticks);
985                 i += sizeof(uvlong);
986         }
987         if(n >= 8){
988                 vlong2le(b, nsec);
989                 i += sizeof(vlong);
990         }
991         return i;
992 }
993
994 /*
995  *  set any of the following
996  *      - time in nsec
997  *      - nsec trim applied over some seconds
998  *      - clock frequency
999  */
1000 static int
1001 writebintime(char *buf, int n)
1002 {
1003         uchar *p;
1004         vlong delta;
1005         long period;
1006
1007         n--;
1008         p = (uchar*)buf + 1;
1009         switch(*buf){
1010         case 'n':
1011                 if(n < sizeof(vlong))
1012                         error(Ebadtimectl);
1013                 le2vlong(&delta, p);
1014                 todset(delta, 0, 0);
1015                 break;
1016         case 'd':
1017                 if(n < sizeof(vlong)+sizeof(long))
1018                         error(Ebadtimectl);
1019                 p = le2vlong(&delta, p);
1020                 le2long(&period, p);
1021                 todset(-1, delta, period);
1022                 break;
1023         case 'f':
1024                 if(n < sizeof(uvlong))
1025                         error(Ebadtimectl);
1026                 le2vlong(&fasthz, p);
1027                 if(fasthz <= 0)
1028                         error(Ebadtimectl);
1029                 todsetfreq(fasthz);
1030                 break;
1031         }
1032         return n;
1033 }