syscall: utility overhaul

Following is a list of functional changes:

* The -o flag outputs the entire buffer to the length returned
  by the syscall, or, in case of fd2path(2) and errstr(2), to '\0'.
* The -x flag is removed; the above makes it possible to pipe
  into xd(1) to get the same result.
* The -s flag uses dirfmt(2) to format the stat message, instead
  of trying to imitate ls(1).
* Stderr reports are normalized and made easier to parse.

The code also suffered a number of stylistic changes.
This commit is contained in:
kvik 2020-09-20 14:49:12 +02:00
parent e3166b4fe8
commit aa7c8cac11
3 changed files with 139 additions and 174 deletions

View file

@ -4,7 +4,7 @@ syscall \- test a system call
.SH SYNOPSIS
.B syscall
[
.B -osx
.B -os
]
.I entry
[
@ -15,39 +15,44 @@ syscall \- test a system call
invokes the system call
.I entry
with the given arguments.
(Some functions, such as
.I write
and
.IR read (2),
although not strictly system calls, are valid
.IR entries .)
It prints the return value and the error string, if there was an error.
An argument is either an integer constant as in C (its value is passed),
a string (its address is passed),
or the literal
.B buf
(a pointer to a 1MB buffer is passed).
The return value is printed.
If an error occured, the error string is also printed.
.PP
If
.B -o
is given, the contents of the 1MB buffer are printed as a zero-terminated string
after the system call is done.
The
.B -x
For convenience,
.IR write (2)
and
.IR read (2)
are included in
.IR entries ,
even though they are not strictly syscalls.
.PP
.I Syscall
arguments are integer constants, strings, or the literal
.BR buf .
The literal
.B buf
refers to a writable 1 megabyte buffer.
Strings and
.B buf
are passed as pointers.
Integers are passed as values.
.PP
The
.B -o
option prints contents of the 1MB buffer.
For
.IR errstr (2)
and
.IR fd2path (2),
the buffer is treated as a 0-terminated string.
For other calls, the number of bytes printed is
determined by the system call's return value.
.PP
The
.B -s
options are similar, but
.B -x
formats the data as hexadecimal bytes, while
.B -s
interprets the data as a
option is similar, but interprets the data as a
.IR stat (5)
message and formats it similar to the style of
.B ls
.B -lqm
(see
.IR ls (1)),
with extra detail about the modify and access times.
message and formats it to standard output.
.SH EXAMPLES
Write a string to standard output:
.IP

View file

@ -1,5 +1,4 @@
</$objtype/mkfile
CFLAGS=-I/sys/src/libc/9syscall $CFLAGS
TARG=syscall
OFILES=syscall.$O\
@ -18,12 +17,24 @@ UPDATE=\
SYSCALL=/sys/src/libc/9syscall/sys.h
tab.h: $SYSCALL
sed '/#define._X[0-9_]/d;
/#define.NSYSCALL/d;
s/#define.([A-Z0-9_][A-Z0-9_]*).*/ "\1", (int(*)(...))\1,/' $SYSCALL |
tr A-Z a-z > tab.h
echo ' "read", (int(*)(...))read,' >> tab.h
echo ' "write", (int(*)(...))write,' >> tab.h
awk '
BEGIN{ print "enum{" }
{ printf "%s, ", $2 }
END{
print "READ, WRITE, NTAB"
print "};"
}' <$SYSCALL >$target
awk '
BEGIN{ print "struct Call tab[] = {" }
{ printf "[%s] \"%s\", (int(*)(...))%s,\n",
$2, tolower($2), tolower($2)
}
END{
print "[READ] \"read\", (int(*)(...))read,"
print "[WRITE] \"write\", (int(*)(...))write,"
print "[NTAB] nil, 0"
print "};"
}' <$SYSCALL >>$target
clean:V:
rm -f *.[$OS] [$OS].out $TARG $HFILES

View file

@ -1,10 +1,9 @@
#include <u.h>
#include <libc.h>
#include <sys.h>
#include <fcall.h>
char buf[1048576];
#define NARG 5
enum{ NARG = 5 };
uintptr arg[NARG];
/* system calls not defined in libc.h */
@ -28,145 +27,18 @@ int _mount(int, char*, int, char*);
int _wait(void*);
int _nsec(vlong*);
struct{
struct Call{
char *name;
int (*func)(...);
}tab[]={
#include "tab.h"
0, 0
};
uintptr parse(char *);
void catch(void*, char*);
char*
xctime(ulong t)
{
char *buf, *s;
s = ctime(t);
s[strlen(s)-1] = '\0'; /* remove newline */
buf = malloc(512);
if(buf == nil)
sysfatal("can't malloc: %r");
snprint(buf, 512, "%s (%lud)", s, t);
return buf;
}
char*
lstime(long l)
{
static char buf[32];
char *t;
long clk;
clk = time(0);
t = ctime(l);
/* 6 months in the past or a day in the future */
if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
memmove(buf, t+4, 7); /* month and day */
memmove(buf+7, t+23, 5); /* year */
}else
memmove(buf, t+4, 12); /* skip day of week */
buf[12] = 0;
return buf;
}
#include "tab.h"
void
main(int argc, char *argv[])
usage(void)
{
int i, j, c;
int oflag, xflag, sflag;
vlong r;
Dir d;
char strs[1024];
char ebuf[1024];
fmtinstall('M', dirmodefmt);
oflag = 0;
xflag = 0;
sflag = 0;
ARGBEGIN{
case 'o':
oflag++;
break;
case 's':
sflag++;
break;
case 'x':
xflag++;
break;
default:
goto Usage;
}ARGEND
if(argc<1 || argc>1+NARG){
Usage:
fprint(2, "usage: syscall [-ox] entry [args; buf==1MB buffer]\n");
fprint(2, "\tsyscall write 1 hello 5\n");
fprint(2, "\tsyscall -o errstr buf 1024\n");
fprint(2, "\tsyscall -[xs] stat file buf 1024\n");
fprint(2, "usage: %s [-os] entry [arg ...]\n", argv0);
exits("usage");
}
for(i=1; i<argc; i++)
arg[i-1] = parse(argv[i]);
notify(catch);
for(i=0; tab[i].name; i++)
if(strcmp(tab[i].name, argv[0])==0){
/* special case for seek, pread, pwrite; vlongs are problematic */
if(strcmp(argv[0], "seek") == 0)
r=seek(arg[0], strtoll(argv[2], 0, 0), arg[2]);
else if(strcmp(argv[0], "pread") == 0)
r=pread(arg[0], (void*)arg[1], arg[2], strtoll(argv[4], 0, 0));
else if(strcmp(argv[0], "pwrite") == 0)
r=pwrite(arg[0], (void*)arg[1], arg[2], strtoll(argv[4], 0, 0));
else
r=(*tab[i].func)(arg[0], arg[1], arg[2], arg[3], arg[4]);
if(r == -1){
errstr(ebuf, sizeof ebuf);
fprint(2, "syscall: return %lld, error:%s\n", r, ebuf);
}else{
ebuf[0] = 0;
fprint(2, "syscall: return %lld, no error\n", r);
}
if(oflag)
print("%s", buf);
if(xflag){
for(j=0; j<r; j++){
if(j%16 == 0)
print("%.4x\t", j);
c = buf[j]&0xFF;
if('!'<=c && c<='~')
print(" %c ", c);
else
print("%.2ux ", c);
if(j%16 == 15)
print("\n");
}
print("\n");
}
if(sflag && r > 0){
r = convM2D((uchar*)buf, r, &d, strs);
if(r <= BIT16SZ)
print("short stat message\n");
else{
print("[%s] ", d.muid);
print("(%.16llux %lud %.2ux) ", d.qid.path, d.qid.vers, d.qid.type);
print("%M (%luo) ", d.mode, d.mode);
print("%c %d ", d.type, d.dev);
print("%s %s ", d.uid, d.gid);
print("%lld ", d.length);
print("%s ", lstime(d.mtime));
print("%s\n", d.name);
print("\tmtime: %s\n\tatime: %s\n", xctime(d.mtime), xctime(d.atime));
}
}
exits(ebuf);
}
fprint(2, "syscall: %s not known\n", argv[0]);
exits("unknown");
}
uintptr
parse(char *s)
@ -174,18 +46,95 @@ parse(char *s)
char *t;
uintptr l;
if(strcmp(s, "buf") == 0)
if(strncmp(s, "buf", 3) == 0)
return (uintptr)buf;
l = strtoull(s, &t, 0);
if(t > s && *t == 0)
return l;
return (uintptr)s;
}
void
catch(void *, char *msg)
{
fprint(2, "syscall: received note='%s'\n", msg);
fprint(2, "syscall: received note: %s\n", msg);
noted(NDFLT);
}
void
main(int argc, char *argv[])
{
int i;
int oflag, sflag;
vlong r, nbuf;
Dir d;
char strs[1024];
char ebuf[ERRMAX];
fmtinstall('D', dirfmt);
oflag = 0;
sflag = 0;
ARGBEGIN{
case 'o':
oflag++;
break;
case 's':
sflag++;
break;
default:
usage();
}ARGEND
if(argc < 1 || argc > 1+NARG)
usage();
for(i = 1; i < argc; i++)
arg[i-1] = parse(argv[i]);
for(i = 0; tab[i].name; i++)
if(strcmp(tab[i].name, argv[0]) == 0)
break;
if(i == NTAB){
fprint(2, "syscall: %s not known\n", argv[0]);
exits("unknown");
}
notify(catch);
/* special case for seek, pread, pwrite; vlongs are problematic */
switch(i){
default:
r = (*tab[i].func)(arg[0], arg[1], arg[2], arg[3], arg[4]);
break;
case SEEK:
r = seek(arg[0], strtoll(argv[2], 0, 0), arg[2]);
break;
case PREAD:
r = pread(arg[0], (void*)arg[1], arg[2], strtoll(argv[4], 0, 0));
break;
case PWRITE:
r = pwrite(arg[0], (void*)arg[1], arg[2], strtoll(argv[4], 0, 0));
break;
}
if(r == -1){
errstr(ebuf, sizeof ebuf);
fprint(2, "syscall: return: %lld error: %s\n", r, ebuf);
exits(ebuf);
}
fprint(2, "syscall: return: %lld\n", r);
if(oflag){
nbuf = r;
switch(i){
case _ERRSTR: case ERRSTR: case FD2PATH:
nbuf = strlen(buf);
}
if(write(1, buf, nbuf) != nbuf)
sysfatal("write: %r");
}else if(sflag){
r = convM2D((uchar*)buf, r, &d, strs);
if(r <= BIT16SZ)
print("short stat message\n");
else
print("%D\n", &d);
}
exits(nil);
}