1 /* Copyight ©2007-2010 Kris Maglione <fbsdaemon@gmail.com>
2  * See LICENSE file for license details.
3  */
4 #define EXTERN
5 #define IXP_NO_P9_
6 #define IXP_P9_STRUCTS
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include <ixp.h>
13 #include <util.h>
14 #include <fmt.h>
15 
16 static IxpClient *client;
17 
18 static void
usage(void)19 usage(void) {
20 	fprint(1,
21 	       "usage: %s [-a <address>] {create | ls [-dlp] | read | remove | write} <file>\n"
22 	       "       %s [-a <address>] xwrite <file> <data>\n"
23 	       "       %s -v\n", argv0, argv0, argv0);
24 	exit(1);
25 }
26 
27 static int
errfmt(Fmt * f)28 errfmt(Fmt *f) {
29 	return fmtstrcpy(f, ixp_errbuf());
30 }
31 
32 /* Utility Functions */
33 static void
write_data(IxpCFid * fid,char * name)34 write_data(IxpCFid *fid, char *name) {
35 	void *buf;
36 	int len;
37 
38 	buf = emalloc(fid->iounit);;
39 	for(;;) {
40 		len = read(0, buf, fid->iounit);
41 		if(len <= 0)
42 			break;
43 		if(ixp_write(fid, buf, len) != len)
44 			fatal("cannot write file %q\n", name);
45 	}
46 	free(buf);
47 }
48 
49 static int
comp_stat(const void * s1,const void * s2)50 comp_stat(const void *s1, const void *s2) {
51 	Stat *st1, *st2;
52 
53 	st1 = (Stat*)s1;
54 	st2 = (Stat*)s2;
55 	return strcmp(st1->name, st2->name);
56 }
57 
58 static void
setrwx(long m,char * s)59 setrwx(long m, char *s) {
60 	static char *modes[] = {
61 		"---", "--x", "-w-",
62 		"-wx", "r--", "r-x",
63 		"rw-", "rwx",
64 	};
65 	strncpy(s, modes[m], 3);
66 }
67 
68 static char *
modestr(uint mode)69 modestr(uint mode) {
70 	static char buf[16];
71 
72 	buf[0]='-';
73 	if(mode & P9_DMDIR)
74 		buf[0]='d';
75 	buf[1]='-';
76 	setrwx((mode >> 6) & 7, &buf[2]);
77 	setrwx((mode >> 3) & 7, &buf[5]);
78 	setrwx((mode >> 0) & 7, &buf[8]);
79 	buf[11] = 0;
80 	return buf;
81 }
82 
83 static char*
timestr(time_t val)84 timestr(time_t val) {
85 	static char buf[32];
86 
87 	strftime(buf, sizeof buf, "%Y-%m-%d %H:%M", localtime(&val));
88 	return buf;
89 }
90 
91 static void
print_stat(Stat * s,int lflag,char * file,int pflag)92 print_stat(Stat *s, int lflag, char *file, int pflag) {
93 	char *slash;
94 
95 	slash = "";
96 	if(pflag)
97 		slash = "/";
98 	else
99 		file = "";
100 
101 	if(lflag)
102 		print("%s %s %s %5llud %s %s%s%s\n",
103 				modestr(s->mode), s->uid, s->gid, s->length,
104 				timestr(s->mtime), file, slash, s->name);
105 	else {
106 		if((s->mode&P9_DMDIR) && strcmp(s->name, "/"))
107 			print("%s%s%s/\n", file, slash, s->name);
108 		else
109 			print("%s%s%s\n", file, slash, s->name);
110 	}
111 }
112 
113 /* Service Functions */
114 static int
xwrite(int argc,char * argv[])115 xwrite(int argc, char *argv[]) {
116 	IxpCFid *fid;
117 	char *file;
118 
119 	ARGBEGIN{
120 	default:
121 		usage();
122 	}ARGEND;
123 
124 	file = EARGF(usage());
125 	fid = ixp_open(client, file, P9_OWRITE);
126 	if(fid == nil)
127 		fatal("Can't open file '%s': %r\n", file);
128 
129 	write_data(fid, file);
130 	ixp_close(fid);
131 	return 0;
132 }
133 
134 static int
xawrite(int argc,char * argv[])135 xawrite(int argc, char *argv[]) {
136 	IxpCFid *fid;
137 	char *file, *buf;
138 	int nbuf, i;
139 
140 	ARGBEGIN{
141 	default:
142 		usage();
143 	}ARGEND;
144 
145 	file = EARGF(usage());
146 	fid = ixp_open(client, file, P9_OWRITE);
147 	if(fid == nil)
148 		fatal("Can't open file '%s': %r\n", file);
149 
150 	nbuf = 1;
151 	for(i=0; i < argc; i++)
152 		nbuf += strlen(argv[i]) + (i > 0);
153 	buf = emalloc(nbuf);
154 	buf[0] = '\0';
155 	while(argc) {
156 		strcat(buf, ARGF());
157 		if(argc)
158 			strcat(buf, " ");
159 	}
160 
161 	if(ixp_write(fid, buf, nbuf) == -1)
162 		fatal("cannot write file '%s': %r\n", file);
163 	ixp_close(fid);
164 	free(buf);
165 	return 0;
166 }
167 
168 static int
xcreate(int argc,char * argv[])169 xcreate(int argc, char *argv[]) {
170 	IxpCFid *fid;
171 	char *file;
172 
173 	ARGBEGIN{
174 	default:
175 		usage();
176 	}ARGEND;
177 
178 	file = EARGF(usage());
179 	fid = ixp_create(client, file, 0777, P9_OWRITE);
180 	if(fid == nil)
181 		fatal("Can't create file '%s': %r\n", file);
182 
183 	if((fid->qid.type&P9_DMDIR) == 0)
184 		write_data(fid, file);
185 	ixp_close(fid);
186 	return 0;
187 }
188 
189 static int
xremove(int argc,char * argv[])190 xremove(int argc, char *argv[]) {
191 	char *file;
192 
193 	ARGBEGIN{
194 	default:
195 		usage();
196 	}ARGEND;
197 
198 	file = EARGF(usage());
199 	do {
200 		if(!ixp_remove(client, file))
201 			fprint(2, "%s: Can't remove file '%s': %r\n", argv0, file);
202 	}while((file = ARGF()));
203 	return 0;
204 }
205 
206 static int
xread(int argc,char * argv[])207 xread(int argc, char *argv[]) {
208 	IxpCFid *fid;
209 	char *file, *buf;
210 	int count;
211 
212 	ARGBEGIN{
213 	default:
214 		usage();
215 	}ARGEND;
216 
217 	if(argc == 0)
218 		usage();
219 	file = EARGF(usage());
220 	do {
221 		fid = ixp_open(client, file, P9_OREAD);
222 		if(fid == nil)
223 			fatal("Can't open file '%s': %r\n", file);
224 
225 		buf = emalloc(fid->iounit);
226 		while((count = ixp_read(fid, buf, fid->iounit)) > 0)
227 			write(1, buf, count);
228 		ixp_close(fid);
229 
230 		if(count == -1)
231 			fprint(2, "%s: cannot read file '%s': %r\n", argv0, file);
232 	} while((file = ARGF()));
233 
234 	return 0;
235 }
236 
237 static int
xls(int argc,char * argv[])238 xls(int argc, char *argv[]) {
239 	IxpMsg m;
240 	Stat *stat;
241 	IxpCFid *fid;
242 	char *file;
243 	char *buf;
244 	int lflag, dflag, pflag;
245 	int count, nstat, mstat, i;
246 
247 	lflag = dflag = pflag = 0;
248 
249 	ARGBEGIN{
250 	case 'l':
251 		lflag++;
252 		break;
253 	case 'd':
254 		dflag++;
255 		break;
256 	case 'p':
257 		pflag++;
258 		break;
259 	default:
260 		usage();
261 	}ARGEND;
262 
263 	count = 0;
264 	file = EARGF(usage());
265 	do {
266 		stat = ixp_stat(client, file);
267 		if(stat == nil)
268 			fatal("cannot stat file '%s': %r\n", file);
269 
270 		i = strlen(file);
271 		if(file[i-1] == '/') {
272 			file[i-1] = '\0';
273 			if(!(stat->mode&P9_DMDIR))
274 				fatal("%s: not a directory", file);
275 		}
276 		if(dflag || (stat->mode&P9_DMDIR) == 0) {
277 			print_stat(stat, lflag, file, pflag);
278 			ixp_freestat(stat);
279 			continue;
280 		}
281 		ixp_freestat(stat);
282 
283 		fid = ixp_open(client, file, P9_OREAD);
284 		if(fid == nil)
285 			fatal("Can't open file '%s': %r\n", file);
286 
287 		nstat = 0;
288 		mstat = 16;
289 		stat = emalloc(mstat * sizeof *stat);
290 		buf = emalloc(fid->iounit);
291 		while((count = ixp_read(fid, buf, fid->iounit)) > 0) {
292 			m = ixp_message(buf, count, MsgUnpack);
293 			while(m.pos < m.end) {
294 				if(nstat == mstat) {
295 					mstat <<= 1;
296 					stat = erealloc(stat, mstat * sizeof *stat);
297 				}
298 				ixp_pstat(&m, &stat[nstat++]);
299 			}
300 		}
301 		ixp_close(fid);
302 
303 		qsort(stat, nstat, sizeof *stat, comp_stat);
304 		for(i = 0; i < nstat; i++) {
305 			print_stat(&stat[i], lflag, file, pflag);
306 			ixp_freestat(&stat[i]);
307 		}
308 		free(stat);
309 	} while((file = ARGF()));
310 
311 	if(count == -1)
312 		fatal("cannot read directory '%s': %r\n", file);
313 	return 0;
314 }
315 
316 static int
xnamespace(int argc,char * argv[])317 xnamespace(int argc, char *argv[]) {
318 	char *path;
319 
320 	ARGBEGIN{
321 	default:
322 		usage();
323 	}ARGEND;
324 
325 	path = ixp_namespace();
326 	if(path == nil)
327 		fatal("can't find namespace: %r\n");
328 	print("%s\n", path);
329 	return 0;
330 }
331 
332 static int
xsetsid(int argc,char * argv[])333 xsetsid(int argc, char *argv[]) {
334 	char *av0;
335 
336 	av0 = nil;
337 	ARGBEGIN{
338 	case '0':
339 		av0 = EARGF(usage());
340 		break;
341 	default:
342 		usage();
343 	}ARGEND;
344 	if(av0 == nil)
345 		av0 = argv[0];
346 	if(av0 == nil)
347 		return 1;
348 
349 	setsid();
350 	execvp(av0, argv);
351 	fatal("setsid: can't exec: %r");
352 	return 1; /* NOTREACHED */
353 }
354 
355 typedef struct exectab exectab;
356 struct exectab {
357 	char *cmd;
358 	int (*fn)(int, char**);
359 } fstab[] = {
360 	{"cat", xread},
361 	{"create", xcreate},
362 	{"ls", xls},
363 	{"read", xread},
364 	{"remove", xremove},
365 	{"rm", xremove},
366 	{"write", xwrite},
367 	{"xwrite", xawrite},
368 	{0, }
369 }, utiltab[] = {
370 	{"namespace", xnamespace},
371 	{"ns", xnamespace},
372 	{"setsid", xsetsid},
373 	{0, }
374 };
375 
376 int
main(int argc,char * argv[])377 main(int argc, char *argv[]) {
378 	char *address;
379 	exectab *tab;
380 	int ret;
381 
382 	quotefmtinstall();
383 	fmtinstall('r', errfmt);
384 
385 	address = getenv("WMII_ADDRESS");
386 
387 	ARGBEGIN{
388 	case 'v':
389 		print("%s-" VERSION ", " COPYRIGHT "\n", argv0);
390 		exit(0);
391 	case 'a':
392 		address = EARGF(usage());
393 		break;
394 	default:
395 		usage();
396 	}ARGEND;
397 
398 	if(argc < 1)
399 		usage();
400 
401 	for(tab=utiltab; tab->cmd; tab++)
402 		if(!strcmp(*argv, tab->cmd))
403 			return tab->fn(argc, argv);
404 
405 	if(address && *address)
406 		client = ixp_mount(address);
407 	else
408 		client = ixp_nsmount("wmii");
409 	if(client == nil)
410 		fatal("can't mount: %r\n");
411 
412 	for(tab=fstab; tab->cmd; tab++)
413 		if(strcmp(*argv, tab->cmd) == 0) break;
414 	if(tab->cmd == 0)
415 		usage();
416 
417 	ret = tab->fn(argc, argv);
418 
419 	ixp_unmount(client);
420 	return ret;
421 }
422 
423