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