examples/ixpsrv.c
author Kris Maglione <kris@suckless.org>
Wed Jul 07 22:32:08 2010 +0000 (8 weeks ago)
changeset 139 38b34b489869
parent 106 8002f61789f4
permissions -rw-r--r--
[debian] Fix DESTDIR. Fix some lintian warnings.
     1 /* Copyright ©2007 Ron Minnich <rminnich at gmail dot com>
     2 /* Copyright ©2007-2010 Kris Maglione <maglione.k@gmail.com>
     3  * See LICENSE file for license details.
     4  */
     5 
     6 /* This is a simple 9P file server which serves a normal filesystem
     7  * hierarchy. While some of the code is from wmii, the server is by
     8  * Ron.
     9  *
    10  * Note: I added an ifdef for Linux vs. BSD for the mount call, so
    11  * this compiles on BSD, but it won't actually run. It should,
    12  * ideally, have the option of not mounting the FS.
    13  *   --Kris
    14  */
    15 #include <assert.h>
    16 #include <dirent.h>
    17 #include <err.h>
    18 #include <errno.h>
    19 #include <fcntl.h>
    20 #include <stdint.h>
    21 #include <stdio.h>
    22 #include <stdlib.h>
    23 #include <string.h>
    24 #include <sys/param.h>
    25 #include <sys/mount.h>
    26 #include <sys/socket.h>
    27 #include <sys/stat.h>
    28 #include <sys/types.h>
    29 #include <time.h>
    30 #include <unistd.h>
    31 
    32 #include <ixp_local.h>
    33 
    34 /* Temporary */
    35 #define fatal(...) ixp_eprint("ixpsrv: fatal: " __VA_ARGS__)
    36 #define debug(...) if(debuglevel) fprintf(stderr, "ixpsrv: " __VA_ARGS__)
    37 
    38 /* Datatypes: */
    39 typedef struct FidAux FidAux;
    40 struct FidAux {
    41 	DIR *dir;
    42 	int fd;
    43 	char name[]; /* c99 */
    44 };
    45 
    46 /* Error messages */
    47 static char
    48 	Enoperm[] = "permission denied",
    49 	Enofile[] = "file not found",
    50 	Ebadvalue[] = "bad value";
    51 /* Macros */
    52 #define QID(t, i) ((vlong)(t))
    53 
    54 /* Global Vars */
    55 static IxpServer server;
    56 static char *user;
    57 static int debuglevel = 0;
    58 
    59 static void fs_open(Ixp9Req *r);
    60 static void fs_walk(Ixp9Req *r);
    61 static void fs_read(Ixp9Req *r);
    62 static void fs_stat(Ixp9Req *r);
    63 static void fs_write(Ixp9Req *r);
    64 static void fs_clunk(Ixp9Req *r);
    65 static void fs_flush(Ixp9Req *r);
    66 static void fs_attach(Ixp9Req *r);
    67 static void fs_create(Ixp9Req *r);
    68 static void fs_remove(Ixp9Req *r);
    69 static void fs_freefid(IxpFid *f);
    70 
    71 Ixp9Srv p9srv = {
    72 	.open=	fs_open,
    73 	.walk=	fs_walk,
    74 	.read=	fs_read,
    75 	.stat=	fs_stat,
    76 	.write=	fs_write,
    77 	.clunk=	fs_clunk,
    78 	.flush=	fs_flush,
    79 	.attach=fs_attach,
    80 	.create=fs_create,
    81 	.remove=fs_remove,
    82 	.freefid=fs_freefid
    83 };
    84 
    85 static void
    86 usage() {
    87 	fprintf(stderr,
    88 		   "usage: %1$s [-a <address>] {create | read | ls [-ld] | remove | write} <file>\n"
    89 		   "       %1$s [-a <address>] xwrite <file> <data>\n"
    90 		   "       %1$s -v\n", argv0);
    91 	exit(1);
    92 }
    93 
    94 
    95 /* Utility Functions */
    96 
    97 static FidAux*
    98 newfidaux(char *name) {
    99 	FidAux *f;
   100 
   101 	f = ixp_emallocz(sizeof(*f) + strlen(name) + 1);
   102 	f->fd = -1;
   103 	strcpy(f->name, name);
   104 	return f;
   105 }
   106 /* is this a dir? */
   107 /* -1 means it ain't anything .. */
   108 static int
   109 isdir(char *path) {
   110 	struct stat buf;
   111 
   112 	if (stat(path, &buf) < 0)
   113 		return -1;
   114 
   115 	return S_ISDIR(buf.st_mode);
   116 }
   117 
   118 /* This should be moved to libixp */
   119 static void
   120 write_buf(Ixp9Req *r, char *buf, uint len) {
   121 
   122 	if(r->ifcall.tread.offset >= len)
   123 		return;
   124 
   125 	len -= r->ifcall.tread.offset;
   126 	if(len > r->ifcall.tread.count)
   127 		len = r->ifcall.tread.count;
   128 	r->ofcall.rread.data = ixp_emalloc(len);
   129 	memcpy(r->ofcall.rread.data, buf + r->ifcall.tread.offset, len);
   130 	r->ofcall.rread.count = len;
   131 }
   132 
   133 
   134 /* This should be moved to libixp */
   135 static void
   136 write_to_buf(Ixp9Req *r, void *buf, uint *len, uint max) {
   137 	uint offset, count;
   138 
   139 //	offset = (r->fid->omode&OAPPEND) ? *len : r->ifcall.tread.offset;
   140 	offset = r->ifcall.tread.offset;
   141 	if(offset > *len || r->ifcall.tread.count == 0) {
   142 		r->ofcall.rread.count = 0;
   143 		return;
   144 	}
   145 
   146 	count = r->ifcall.tread.count;
   147 	if(max && (count > max - offset))
   148 		count = max - offset;
   149 
   150 	*len = offset + count;
   151 
   152 	if(max == 0) {
   153 		*(void **)buf = ixp_erealloc(*(void **)buf, *len + 1);
   154 		buf = *(void **)buf;
   155 	}
   156 
   157 	memcpy((uchar*)buf + offset, r->ifcall.tread.data, count);
   158 	r->ofcall.rread.count = count;
   159 	((char *)buf)[offset+count] = '\0';
   160 }
   161 
   162 static void
   163 dostat(Stat *s, char *name, struct stat *buf) {
   164 
   165 	s->type = 0;
   166 	s->dev = 0;
   167 	s->qid.type = buf->st_mode&S_IFMT;
   168 	s->qid.path = buf->st_ino;
   169 	s->qid.version = 0;
   170 	s->mode = buf->st_mode & 0777;
   171 	if (S_ISDIR(buf->st_mode)) {
   172 		s->mode |= P9_DMDIR;
   173 		s->qid.type |= QTDIR;
   174 	}
   175 	s->atime = buf->st_atime;
   176 	s->mtime = buf->st_mtime;
   177 	s->length = buf->st_size;
   178 	s->name =name;
   179 	s->uid = user;
   180 	s->gid = user;
   181 	s->muid = user;
   182 }
   183 
   184 /* the gnu/linux guys have made a real mess of errno ... don't ask --ron */
   185 /* I agree. --Kris */
   186 void
   187 rerrno(Ixp9Req *r, char *m) {
   188 /*
   189 	char errbuf[128];
   190 	respond(r, strerror_r(errno, errbuf, sizeof(errbuf)));
   191  */
   192 	respond(r, m);
   193 }
   194 
   195 void
   196 fs_attach(Ixp9Req *r) {
   197 
   198 	debug("fs_attach(%p)\n", r);
   199 
   200 	r->fid->qid.type = QTDIR;
   201 	r->fid->qid.path = (uintptr_t)r->fid;
   202 	r->fid->aux = newfidaux("/");
   203 	r->ofcall.rattach.qid = r->fid->qid;
   204 	respond(r, nil);
   205 }
   206 
   207 void
   208 fs_walk(Ixp9Req *r) {
   209 	struct stat buf;
   210 	char *name;
   211 	FidAux *f;
   212 	int i;
   213 	
   214 	debug("fs_walk(%p)\n", r);
   215 
   216 	f = r->fid->aux;
   217 	name = malloc(PATH_MAX);
   218 	strcpy(name, f->name);
   219 	if (stat(name, &buf) < 0){
   220 		respond(r, Enofile);
   221 		return;
   222 	}
   223 
   224 	/* build full path. Stat full path. Done */
   225 	for(i=0; i < r->ifcall.twalk.nwname; i++) {
   226 		strcat(name, "/");
   227 		strcat(name, r->ifcall.twalk.wname[i]);
   228 		if (stat(name, &buf) < 0){
   229 			respond(r, Enofile);
   230 			free(name);
   231 			return;
   232 		}
   233 		r->ofcall.rwalk.wqid[i].type = buf.st_mode&S_IFMT;
   234 		r->ofcall.rwalk.wqid[i].path = buf.st_ino;
   235 	}
   236 
   237 	r->newfid->aux = newfidaux(name);
   238 	r->ofcall.rwalk.nwqid = i;
   239 	free(name);
   240 	respond(r, nil);
   241 }
   242 
   243 void
   244 fs_stat(Ixp9Req *r) {
   245 	struct stat st;
   246 	Stat s;
   247 	IxpMsg m;
   248 	char *name;
   249 	uchar *buf;
   250 	FidAux *f;
   251 	int size;
   252 	
   253 	f = r->fid->aux;
   254 	debug("fs_stat(%p)\n", r);
   255 	debug("fs_stat %s\n", f->name);
   256 
   257 	name = f->name;
   258 	if (stat(name, &st) < 0){
   259 		respond(r, Enofile);
   260 		return;
   261 	}
   262 
   263 	dostat(&s, name, &st);
   264 	r->fid->qid = s.qid;
   265 	r->ofcall.rstat.nstat = size = ixp_sizeof_stat(&s);
   266 
   267 	buf = ixp_emallocz(size);
   268 
   269 	m = ixp_message(buf, size, MsgPack);
   270 	ixp_pstat(&m, &s);
   271 
   272 	r->ofcall.rstat.stat = m.data;
   273 	respond(r, nil);
   274 }
   275 
   276 void
   277 fs_read(Ixp9Req *r) {
   278 	FidAux *f;
   279 	char *buf;
   280 	int n, offset;
   281 	int size;
   282 
   283 	debug("fs_read(%p)\n", r);
   284 
   285 	f = r->fid->aux;
   286 
   287 	if (f->dir) {
   288 		Stat s;
   289 		IxpMsg m;
   290 
   291 		offset = 0;
   292 		size = r->ifcall.tread.count;
   293 		buf = ixp_emallocz(size);
   294 		m = ixp_message((uchar*)buf, size, MsgPack);
   295 
   296 		/* note: we don't really handle lots of things well, so do one thing
   297 		 * at a time 
   298 		 */
   299 		/*for(f=f->next; f; f=f->next) */{
   300 			struct dirent *d;
   301 			struct stat st;
   302 			d = readdir(f->dir);
   303 			if (d) {
   304 				stat(d->d_name, &st);
   305 				dostat(&s, d->d_name, &st);
   306 				n = ixp_sizeof_stat(&s);
   307 				ixp_pstat(&m, &s);
   308 				offset += n;
   309 			} else n = 0;
   310 		}
   311 		r->ofcall.rread.count = n;
   312 		r->ofcall.rread.data = (char*)m.data;
   313 		respond(r, nil);
   314 		return;
   315 	} else {
   316 		r->ofcall.rread.data = ixp_emallocz(r->ifcall.tread.count);
   317 		if (! r->ofcall.rread.data) {
   318 			respond(r, nil);
   319 			return;
   320 		}
   321 		r->ofcall.rread.count = pread(f->fd, r->ofcall.rread.data, r->ifcall.tread.count, r->ifcall.tread.offset);
   322 		if (r->ofcall.rread.count < 0) 
   323 			rerrno(r, Enoperm);
   324 		else
   325 			respond(r, nil);
   326 		return;
   327 	}
   328 
   329 	/* 
   330 	 * This is an assert because this should this should not be called if
   331 	 * the file is not open for reading.
   332 	 */
   333 	assert(!"Read called on an unreadable file");
   334 }
   335 
   336 void
   337 fs_write(Ixp9Req *r) {
   338 	FidAux *f;
   339 
   340 	debug("fs_write(%p)\n", r);
   341 
   342 	if(r->ifcall.twrite.count == 0) {
   343 		respond(r, nil);
   344 		return;
   345 	}
   346 	f = r->fid->aux;
   347 	/*
   348 	 * This is an assert because this function should not be called if
   349 	 * the file is not open for writing.
   350 	 */
   351 	assert(!"Write called on an unwritable file");
   352 }
   353 
   354 void
   355 fs_open(Ixp9Req *r) {
   356 	int dir;
   357 	FidAux *f;
   358 
   359 	debug("fs_open(%p)\n", r);
   360 
   361 	f = r->fid->aux;
   362 	dir = isdir(f->name);
   363 	/* fucking stupid linux -- open dir is a DIR */
   364 
   365 	if (dir) {
   366 		f->dir = opendir(f->name);
   367 		if (! f->dir){
   368 			rerrno(r, Enoperm);
   369 			return;
   370 		}
   371 	} else {
   372 		f->fd = open(f->name, O_RDONLY);
   373 		if (f->fd < 0){
   374 			rerrno(r, Enoperm);
   375 			return;
   376 		}
   377 	}
   378 	respond(r, nil);
   379 }
   380 
   381 
   382 void
   383 fs_create(Ixp9Req *r) {
   384 	debug("fs_create(%p)\n", r);
   385 	respond(r, Enoperm);
   386 }
   387 
   388 void
   389 fs_remove(Ixp9Req *r) {
   390 	debug("fs_remove(%p)\n", r);
   391 	respond(r, Enoperm);
   392 
   393 }
   394 
   395 void
   396 fs_clunk(Ixp9Req *r) {
   397 	int dir;
   398 	FidAux *f;
   399 
   400 	debug("fs_clunk(%p)\n", f);
   401 
   402 	f = r->fid->aux;
   403 	dir = isdir(f->name);
   404 	if (dir) {
   405 		(void) closedir(f->dir);
   406 		f->dir = NULL;
   407 	} else {
   408 		(void) close(f->fd);
   409 		f->fd = -1;
   410 	}
   411 
   412 	respond(r, nil);
   413 }
   414 
   415 void
   416 fs_flush(Ixp9Req *r) {
   417 	debug("fs_flush(%p)\n", r);
   418 	respond(r, nil);
   419 }
   420 
   421 void
   422 fs_freefid(Fid *f) {
   423 	debug("fs_freefid(%p)\n", f);
   424 	free(f->aux);
   425 }
   426 
   427 // mount -t 9p 127.1 /tmp/cache -o port=20006,noextend
   428 /* Yuck. */
   429 #if defined(__linux__)
   430 #  define MF(n) MS_##n
   431 #  define mymount(src, dest, flags, opts) mount(src, dest, "9p", flags, opts)
   432 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
   433 #  define MF(n) MNT_##n
   434 #  define mymount(src, dest, flags, opts) mount("9p", dest, flags, src)
   435 #endif
   436 
   437 static ulong mountflags =
   438 	  MF(NOATIME)
   439 	| MF(NODEV)
   440 	/* | MF(NODIRATIME) */
   441 	| MF(NOEXEC)
   442 	| MF(NOSUID)
   443 	| MF(RDONLY);
   444 
   445 int
   446 getaddr(char *mountaddr, char **ip, char **port) {
   447 	char *cp;
   448 
   449 	if (!mountaddr)
   450 		mountaddr = getenv("XCPU_PARENT");
   451 	if (!mountaddr)
   452 		return -1;
   453 
   454 	cp = mountaddr;
   455 	if (strcmp(cp, "tcp!"))
   456 		cp += 4;
   457 
   458 	*ip = cp;
   459 
   460 	cp = strstr(cp, "!");
   461 	if (cp)
   462 		*port = cp + 1;
   463 	return strtoul(*port, 0, 0);
   464 }
   465 
   466 int
   467 main(int argc, char *argv[]) {
   468 	int fd;
   469 	int ret;
   470 	int domount, mountonly;
   471 	char *mountaddr;
   472 	char *address;
   473 	char *msg;
   474 	IxpConn *acceptor;
   475 
   476 	domount = 0;
   477 	mountonly = 0;
   478 	address = getenv("IXP_ADDRESS");
   479 	mountaddr = nil;
   480 
   481 	ARGBEGIN{
   482 	case 'v':
   483 		printf("%s-" VERSION ", ©2007 Ron Minnich\n", argv0);
   484 		exit(0);
   485 	case 'a':
   486 		address = EARGF(usage());
   487 		break;
   488 	case 'd':
   489 		debuglevel++;
   490 		break;
   491 	case 'm':
   492 		domount++;
   493 		break;
   494 	case 'M':
   495 		mountonly++;
   496 		break;
   497 	default:
   498 		usage();
   499 	}ARGEND;
   500 
   501 	if(!address)
   502 		fatal("$IXP_ADDRESS not set\n");
   503 
   504 	if(!(user = getenv("USER")))
   505 		fatal("$USER not set\n");
   506 
   507 	fd = ixp_announce(address);
   508 	if(fd < 0)
   509 		fatal("%s\n", errstr);
   510 
   511 	/* set up a fake client so we can grap connects. */
   512 	acceptor = ixp_listen(&server, fd, &p9srv, serve_9pcon, NULL);
   513 
   514 	/* we might need to mount ourselves. The bit of complexity is the need to fork so 
   515 	 * we can serve ourselves. We've done the listen so that's ok.
   516 	 */
   517 	if (domount){
   518 		int f = fork();
   519 		if (f < 0)
   520 			errx(1, "fork!");
   521 		if (!f){
   522 			char *addr, *aport;
   523 			int port;
   524 			char options[128];
   525 
   526 			port = getaddr(mountaddr, &addr, &aport);
   527 			sprintf(options, "port=%d,noextend", port);
   528 			if (mymount(addr, "/tmp/cache", mountflags, options) < 0)
   529 				errx(1, "Mount failed");
   530 		}
   531 		
   532 	}
   533 
   534 	if (mountonly)
   535 		exit(0);
   536 
   537 	ixp_serverloop(&server);
   538 	printf("msg %s\n", ixp_errbuf());
   539 	return ret;
   540 }
   541