xref: /plan9/sys/src/cmd/con/con.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 
4 #include "rustream.h"
5 #include "ruttyio.h"
6 #include "rusignal.h"
7 #include "rufilio.h"
8 
9 int debug;		/* true if debugging */
10 int ctl = -1;		/* control fd (for break's) */
11 int raw;		/* true if raw is on */
12 int consctl = -1;	/* control fd for cons */
13 int ttypid;		/* pid's if the 2 processes (used to kill them) */
14 int msgfd = -1;		/* mesgld file descriptor (for signals to be written to) */
15 int outfd = 1;		/* local output file descriptor */
16 int cooked;		/* non-zero forces cooked mode */
17 int returns;		/* non-zero forces carriage returns not to be filtered out */
18 int crtonl;			/* non-zero forces carriage returns to be converted to nls coming from net */
19 int	strip;		/* strip off parity bits */
20 char firsterr[2*ERRMAX];
21 char transerr[2*ERRMAX];
22 int limited;
23 char *remuser;
24 int verbose;
25 int baud;
26 int notkbd;
27 int nltocr;		/* translate kbd nl to cr  and vice versa */
28 
29 typedef struct Msg Msg;
30 #define MAXMSG (2*8192)
31 
32 int	dodial(char*, char*, char*);
33 void	fromkbd(int);
34 void	fromnet(int);
35 long	iread(int, void*, int);
36 long	iwrite(int, void*, int);
37 int	menu(int);
38 void	msgfromkbd(int);
39 void	msgfromnet(int);
40 int	msgwrite(int, void*, int);
41 void	notifyf(void*, char*);
42 void	pass(int, int, int);
43 void	rawoff(void);
44 void	rawon(void);
45 int	readupto(int, char*, int);
46 int	sendctl(int, int);
47 int	sendctl1(int, int, int);
48 void	stdcon(int);
49 char*	system(int, char*);
50 void	dosystem(int, char*);
51 int	wasintr(void);
52 void	punt(char*);
53 char*	syserr(void);
54 void	seterr(char*);
55 
56 /* protocols */
57 void	device(char*, char*);
58 void	rlogin(char*, char*);
59 void	simple(char*, char*);
60 
61 void
62 usage(void)
63 {
64 	punt("usage: con [-CdnrRsTv] [-b baud] [-l [user]] [-c cmd] net!host[!service]");
65 }
66 
67 void
68 main(int argc, char *argv[])
69 {
70 	char *dest;
71 	char *cmd = 0;
72 
73 	returns = 1;
74 	ARGBEGIN{
75 	case 'b':
76 		baud = atoi(EARGF(usage()));
77 		break;
78 	case 'd':
79 		debug = 1;
80 		break;
81 	case 'l':
82 		limited = 1;
83 		if(argv[1] != nil && argv[1][0] != '-')
84 			remuser = ARGF();
85 		break;
86 	case 'n':
87 		notkbd = 1;
88 		break;
89 	case 'r':
90 		returns = 0;
91 		break;
92 	case 'R':
93 		nltocr = 1;
94 		break;
95 	case 'T':
96 		crtonl = 1;
97 		break;
98 	case 'C':
99 		cooked = 1;
100 		break;
101 	case 'c':
102 		cmd = ARGF();
103 		break;
104 	case 'v':
105 		verbose = 1;
106 		break;
107 	case 's':
108 		strip = 1;
109 		break;
110 	default:
111 		usage();
112 	}ARGEND
113 
114 	if(argc != 1){
115 		if(remuser == 0)
116 			usage();
117 		dest = remuser;
118 		remuser = 0;
119 	} else
120 		dest = argv[0];
121 	if(*dest == '/' && strchr(dest, '!') == 0)
122 		device(dest, cmd);
123 	else if(limited){
124 		simple(dest, cmd);	/* doesn't return if dialout succeeds */
125 		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
126 	} else {
127 		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
128 		simple(dest, cmd);	/* doesn't return if dialout succeeds */
129 	}
130 	punt(firsterr);
131 }
132 
133 /*
134  *  just dial and use as a byte stream with remote echo
135  */
136 void
137 simple(char *dest, char *cmd)
138 {
139 	int net;
140 
141 	net = dodial(dest, 0, 0);
142 	if(net < 0)
143 		return;
144 
145 	if(cmd)
146 		dosystem(net, cmd);
147 
148 	if(!cooked)
149 		rawon();
150 	stdcon(net);
151 	exits(0);
152 }
153 
154 /*
155  *  dial, do UCB authentication, use as a byte stream with local echo
156  *
157  *  return if dial failed
158  */
159 void
160 rlogin(char *dest, char *cmd)
161 {
162 	int net;
163 	char buf[128];
164 	char *p;
165 	char *localuser;
166 
167 	/* only useful on TCP */
168 	if(strchr(dest, '!')
169 	&& (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0))
170 		return;
171 
172 	net = dodial(dest, "tcp", "login");
173 	if(net < 0)
174 		return;
175 
176 	/*
177 	 *  do UCB rlogin authentication
178 	 */
179 	localuser = getuser();
180 	if(remuser == 0){
181 		if(limited)
182 			remuser = ":";
183 		else
184 			remuser = localuser;
185 	}
186 	p = getenv("TERM");
187 	if(p == 0)
188 		p = "p9";
189 	if(write(net, "", 1)<0
190 	|| write(net, localuser, strlen(localuser)+1)<0
191 	|| write(net, remuser, strlen(remuser)+1)<0
192 	|| write(net, p, strlen(p)+1)<0){
193 		close(net);
194 		punt("BSD authentication failed");
195 	}
196 	if(read(net, buf, 1) != 1)
197 		punt("BSD authentication failed1");
198 	if(buf[0] != 0){
199 		fprint(2, "con: remote error: ");
200 		while(read(net, buf, 1) == 1){
201 			write(2, buf, 1);
202 			if(buf[0] == '\n')
203 				break;
204 		}
205 		exits("read");
206 	}
207 
208 	if(cmd)
209 		dosystem(net, cmd);
210 
211 	if(!cooked)
212 		rawon();
213 	nltocr = 1;
214 	stdcon(net);
215 	exits(0);
216 }
217 
218 /*
219  *  just open a device and use it as a connection
220  */
221 void
222 device(char *dest, char *cmd)
223 {
224 	int net;
225 	char cname[128];
226 
227 	net = open(dest, ORDWR);
228 	if(net < 0) {
229 		fprint(2, "con: cannot open %s: %r\n", dest);
230 		exits("open");
231 	}
232 	snprint(cname, sizeof cname, "%sctl", dest);
233 	ctl = open(cname, ORDWR);
234 	if (baud > 0) {
235 		if(ctl >= 0)
236 			fprint(ctl, "b%d", baud);
237 		else
238 			fprint(2, "con: cannot open %s: %r\n", cname);
239 	}
240 
241 	if(cmd)
242 		dosystem(net, cmd);
243 
244 	if(!cooked)
245 		rawon();
246 	stdcon(net);
247 	exits(0);
248 }
249 
250 /*
251  *  ignore interrupts
252  */
253 void
254 notifyf(void *a, char *msg)
255 {
256 	USED(a);
257 
258 	if(strstr(msg, "yankee"))
259 		noted(NDFLT);
260 	if(strstr(msg, "closed pipe")
261 	|| strcmp(msg, "interrupt") == 0
262 	|| strcmp(msg, "hangup") == 0)
263 		noted(NCONT);
264 	noted(NDFLT);
265 }
266 
267 /*
268  *  turn keyboard raw mode on
269  */
270 void
271 rawon(void)
272 {
273 	if(debug)
274 		fprint(2, "rawon\n");
275 	if(raw)
276 		return;
277 	if(consctl < 0)
278 		consctl = open("/dev/consctl", OWRITE);
279 	if(consctl < 0){
280 //		fprint(2, "can't open consctl\n");
281 		return;
282 	}
283 	write(consctl, "rawon", 5);
284 	raw = 1;
285 }
286 
287 /*
288  *  turn keyboard raw mode off
289  */
290 void
291 rawoff(void)
292 {
293 	if(debug)
294 		fprint(2, "rawoff\n");
295 	if(raw == 0)
296 		return;
297 	if(consctl < 0)
298 		consctl = open("/dev/consctl", OWRITE);
299 	if(consctl < 0){
300 //		fprint(2, "can't open consctl\n");
301 		return;
302 	}
303 	write(consctl, "rawoff", 6);
304 	raw = 0;
305 }
306 
307 /*
308  *  control menu
309  */
310 #define STDHELP	"\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
311 
312 int
313 menu(int net)
314 {
315 	char buf[MAXMSG];
316 	long n;
317 	int done;
318 	int wasraw = raw;
319 
320 	if(wasraw)
321 		rawoff();
322 
323 	fprint(2, ">>> ");
324 	for(done = 0; !done; ){
325 		n = read(0, buf, sizeof(buf)-1);
326 		if(n <= 0)
327 			return -1;
328 		buf[n] = 0;
329 		switch(buf[0]){
330 		case '!':
331 			print(buf);
332 			system(net, buf+1);
333 			print("!\n");
334 			done = 1;
335 			break;
336 		case '.':
337 			done = 1;
338 			break;
339 		case 'q':
340 			return -1;
341 		case 'i':
342 			buf[0] = 0x1c;
343 			if(msgfd <= 0)
344 				write(net, buf, 1);
345 			else
346 				sendctl1(msgfd, M_SIGNAL, SIGQUIT);
347 			done = 1;
348 			break;
349 		case 'b':
350 			if(msgfd >= 0)
351 				sendctl(msgfd, M_BREAK);
352 			else if(ctl >= 0)
353 				write(ctl, "k", 1);
354 			done = 1;
355 			break;
356 		case 'r':
357 			returns = 1-returns;
358 			done = 1;
359 			break;
360 		default:
361 			fprint(2, STDHELP);
362 			break;
363 		}
364 		if(!done)
365 			fprint(2, ">>> ");
366 	}
367 
368 	if(wasraw)
369 		rawon();
370 	else
371 		rawoff();
372 	return 0;
373 }
374 
375 /*
376  *  the real work.  two processes pass bytes back and forth between the
377  *  terminal and the network.
378  */
379 void
380 stdcon(int net)
381 {
382 	int netpid;
383 
384 	ttypid = getpid();
385 	switch(netpid = rfork(RFMEM|RFPROC)){
386 	case -1:
387 		perror("con");
388 		exits("fork");
389 	case 0:
390 		notify(notifyf);
391 		fromnet(net);
392 		postnote(PNPROC, ttypid, "die yankee dog");
393 		exits(0);
394 	default:
395 		notify(notifyf);
396 		fromkbd(net);
397 		if(notkbd)
398 			for(;;)sleep(0);
399 		postnote(PNPROC, netpid, "die yankee dog");
400 		exits(0);
401 	}
402 }
403 
404 /*
405  *  Read the keyboard and write it to the network.  '^\' gets us into
406  *  the menu.
407  */
408 void
409 fromkbd(int net)
410 {
411 	long n;
412 	char buf[MAXMSG];
413 	char *p, *ep;
414 	int eofs;
415 
416 	eofs = 0;
417 	for(;;){
418 		n = read(0, buf, sizeof(buf));
419 		if(n < 0){
420 			if(wasintr()){
421 				if(!raw){
422 					buf[0] = 0x7f;
423 					n = 1;
424 				} else
425 					continue;
426 			} else
427 				return;
428 		}
429 		if(n == 0){
430 			if(++eofs > 32)
431 				return;
432 		} else
433 			eofs = 0;
434 		if(n && memchr(buf, 0x1c, n)){
435 			if(menu(net) < 0)
436 				return;
437 		}else{
438 			if(!raw && n==0){
439 				buf[0] = 0x4;
440 				n = 1;
441 			}
442 			if(nltocr){
443 				ep = buf+n;
444 				for(p = buf; p < ep; p++)
445 					switch(*p){
446 					case '\r':
447 						*p = '\n';
448 						break;
449 					case '\n':
450 						*p = '\r';
451 						break;
452 					}
453 			}
454 			if(iwrite(net, buf, n) != n)
455 				return;
456 		}
457 	}
458 }
459 
460 /*
461  *  Read from the network and write to the screen.
462  *  Filter out spurious carriage returns.
463  */
464 void
465 fromnet(int net)
466 {
467 	long n;
468 	char buf[MAXMSG];
469 	char *cp, *ep;
470 
471 	for(;;){
472 		n = iread(net, buf, sizeof(buf));
473 		if(n < 0)
474 			return;
475 		if(n == 0)
476 			continue;
477 
478 		if (strip)
479 			for (cp=buf; cp<buf+n; cp++)
480 				*cp &= 0177;
481 
482 		if(crtonl) {
483 			/* convert cr's to nl's */
484 			for (cp = buf; cp < buf + n; cp++)
485 				if (*cp == '\r')
486 					*cp = '\n';
487 		}
488 		else if(!returns){
489 			/* convert cr's to null's */
490 			cp = buf;
491 			ep = buf + n;
492 			while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){
493 				memmove(cp, cp+1, ep-cp-1);
494 				ep--;
495 				n--;
496 			}
497 		}
498 
499 		if(n > 0 && iwrite(outfd, buf, n) != n){
500 			if(outfd == 1)
501 				return;
502 			outfd = 1;
503 			if(iwrite(1, buf, n) != n)
504 				return;
505 		}
506 	}
507 }
508 
509 /*
510  *  dial and return a data connection
511  */
512 int
513 dodial(char *dest, char *net, char *service)
514 {
515 	char name[128];
516 	char devdir[128];
517 	int data;
518 
519 	devdir[0] = 0;
520 	strcpy(name, netmkaddr(dest, net, service));
521 	data = dial(name, 0, devdir, &ctl);
522 	if(data < 0){
523 		seterr(name);
524 		return -1;
525 	}
526 	fprint(2, "connected to %s on %s\n", name, devdir);
527 	return data;
528 }
529 
530 void
531 dosystem(int fd, char *cmd)
532 {
533 	char *p;
534 
535 	p = system(fd, cmd);
536 	if(p){
537 		print("con: %s terminated with %s\n", cmd, p);
538 		exits(p);
539 	}
540 }
541 
542 /*
543  *  run a command with the network connection as standard IO
544  */
545 char *
546 system(int fd, char *cmd)
547 {
548 	int pid;
549 	int p;
550 	static Waitmsg msg;
551 	int pfd[2];
552 	int n;
553 	char buf[4096];
554 
555 	if(pipe(pfd) < 0){
556 		perror("pipe");
557 		return "pipe failed";
558 	}
559 	outfd = pfd[1];
560 
561 	close(consctl);
562 	consctl = -1;
563 	switch(pid = fork()){
564 	case -1:
565 		perror("con");
566 		return "fork failed";
567 	case 0:
568 		close(pfd[1]);
569 		dup(pfd[0], 0);
570 		dup(fd, 1);
571 		close(ctl);
572 		close(fd);
573 		close(pfd[0]);
574 		if(*cmd)
575 			execl("/bin/rc", "rc", "-c", cmd, nil);
576 		else
577 			execl("/bin/rc", "rc", nil);
578 		perror("con");
579 		exits("exec");
580 		break;
581 	default:
582 		close(pfd[0]);
583 		while((n = read(pfd[1], buf, sizeof(buf))) > 0){
584 			if(msgfd >= 0){
585 				if(msgwrite(fd, buf, n) != n)
586 					break;
587 			} else {
588 				if(write(fd, buf, n) != n)
589 					break;
590 			}
591 		}
592 		p = waitpid();
593 		outfd = 1;
594 		close(pfd[1]);
595 		if(p < 0 || p != pid)
596 			return "lost child";
597 		break;
598 	}
599 	return msg.msg;
600 }
601 
602 int
603 wasintr(void)
604 {
605 	return strcmp(syserr(), "interrupted") == 0;
606 }
607 
608 void
609 punt(char *msg)
610 {
611 	if(*msg == 0)
612 		msg = transerr;
613 	fprint(2, "con: %s\n", msg);
614 	exits(msg);
615 }
616 
617 char*
618 syserr(void)
619 {
620 	static char err[ERRMAX];
621 	errstr(err, sizeof err);
622 	return err;
623 }
624 
625 void
626 seterr(char *addr)
627 {
628 	char *se = syserr();
629 
630 	if(verbose)
631 		fprint(2, "'%s' calling %s\n", se, addr);
632 	if(firsterr[0] && (strstr(se, "translate") ||
633 	 strstr(se, "file does not exist") ||
634 	 strstr(se, "unknown address") ||
635 	 strstr(se, "directory entry not found")))
636 		return;
637 	strcpy(firsterr, se);
638 }
639 
640 
641 long
642 iread(int f, void *a, int n)
643 {
644 	long m;
645 
646 	for(;;){
647 		m = read(f, a, n);
648 		if(m >= 0 || !wasintr())
649 			break;
650 	}
651 	return m;
652 }
653 
654 long
655 iwrite(int f, void *a, int n)
656 {
657 	long m;
658 
659 	m = write(f, a, n);
660 	if(m < 0 && wasintr())
661 		return n;
662 	return m;
663 }
664 
665 /*
666  *  The rest is to support the V10 mesgld protocol.
667  */
668 
669 /*
670  *  network orderings
671  */
672 #define get2byte(p) ((p)[0] + ((p)[1]<<8))
673 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24))
674 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8)
675 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24)
676 
677 /*
678  *  tty parameters
679  */
680 int sgflags = ECHO;
681 
682 /*
683  *  a mesgld message
684  */
685 struct Msg {
686 	struct mesg h;
687 	char b[MAXMSG];
688 };
689 
690 
691 /*
692  *  send an empty mesgld message
693  */
694 int
695 sendctl(int net, int type)
696 {
697 	Msg m;
698 
699 	m.h.type = type;
700 	m.h.magic = MSGMAGIC;
701 	put2byte(m.h.size, 0);
702 	if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg))
703 		return -1;
704 	return 0;
705 }
706 
707 /*
708  *  send a one byte mesgld message
709  */
710 int
711 sendctl1(int net, int type, int parm)
712 {
713 	Msg m;
714 
715 	m.h.type = type;
716 	m.h.magic = MSGMAGIC;
717 	m.b[0] = parm;
718 	put2byte(m.h.size, 1);
719 	if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1)
720 		return -1;
721 	return 0;
722 }
723 
724 /*
725  *  read n bytes.  return -1 if it fails, 0 otherwise.
726  */
727 int
728 readupto(int from, char *a, int len)
729 {
730 	int n;
731 
732 	while(len > 0){
733 		n = iread(from, a, len);
734 		if(n < 0)
735 			return -1;
736 		a += n;
737 		len -= n;
738 	}
739 	return 0;
740 }
741 
742 /*
743  *  Decode a mesgld message from the network
744  */
745 void
746 msgfromnet(int net)
747 {
748 	ulong com;
749 	struct stioctl *io;
750 	struct sgttyb *sg;
751 	struct ttydevb *td;
752 	struct tchars *tc;
753 	int len;
754 	Msg m;
755 
756 	for(;;){
757 		/* get a complete mesgld message */
758 		if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0)
759 			break;
760 		if(m.h.magic != MSGMAGIC){
761 			fprint(2, "con: bad message magic 0x%ux\n", m.h.magic);
762 			break;
763 		}
764 		len = get2byte(m.h.size);
765 		if(len > sizeof(m.b)){
766 			len = sizeof(m.b);
767 			fprint(2, "con: mesgld message too long\n");
768 		}
769 		if(len && readupto(net, m.b, len) < 0)
770 			break;
771 
772 		/* decode */
773 		switch(m.h.type){
774 		case M_HANGUP:
775 			if(debug)
776 				fprint(2, "M_HANGUP\n");
777 			return;
778 		case M_DATA:
779 			if(debug)
780 				fprint(2, "M_DATA %d bytes\n", len);
781 			if(iwrite(outfd, m.b, len) != len){
782 				if(outfd == 1)
783 					return;
784 				outfd = 1;
785 				if(iwrite(outfd, m.b, len) != len)
786 					return;
787 			}
788 			continue;
789 		case M_IOCTL:
790 			break;
791 		default:
792 			/* ignore */
793 			if(debug)
794 				fprint(2, "con: unknown message\n");
795 			continue;
796 		}
797 
798 		/*
799 		 *  answer an ioctl
800 		 */
801 		io = (struct stioctl *)m.b;
802 		com = get4byte(io->com);
803 		if(debug)
804 			fprint(2, "M_IOCTL %lud\n", com);
805 		switch(com){
806 		case FIOLOOKLD:
807 			put4byte(io->data, tty_ld);
808 			len = 0;
809 			break;
810 		case TIOCGETP:
811 			sg = (struct sgttyb *)io->data;
812 			sg->sg_ispeed = sg->sg_ospeed = B9600;
813 			sg->sg_erase = 0010;	/* back space */
814 			sg->sg_kill = 0025;	/* CNTL U */
815 			put2byte(sg->sg_flags, sgflags);
816 			len = sizeof(struct sgttyb);
817 			break;
818 		case TIOCSETN:
819 		case TIOCSETP:
820 			sg = (struct sgttyb *)io->data;
821 			sgflags = get2byte(sg->sg_flags);
822 			if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO))
823 				rawon();
824 			else
825 				rawoff();
826 			len = 0;
827 			break;
828 		case TIOCGETC:
829 			tc = (struct tchars *)io->data;
830 			tc->t_intrc = 0177;
831 			tc->t_quitc = 0034;
832 			tc->t_startc = 0;
833 			tc->t_stopc = 0;
834 			tc->t_eofc = 0004;
835 			tc->t_brkc = 0;
836 			len = sizeof(struct tchars);
837 			break;
838 		case TIOCSETC:
839 			len = 0;
840 			break;
841 		case TIOCGDEV:
842 			td = (struct ttydevb *)io->data;
843 			td->ispeed = td->ospeed = B9600;
844 			put2byte(td->flags, 0);
845 			len = sizeof(struct ttydevb);
846 			break;
847 		case TIOCSDEV:
848 			len = 0;
849 			break;
850 		default:
851 			/*
852 			 *  unimplemented
853 			 */
854 			m.b[len] = 0;
855 			if(sendctl(net, M_IOCNAK) < 0)
856 				return;
857 			continue;
858 		}
859 
860 		/*
861 		 *  acknowledge
862 		 */
863 		m.h.type = M_IOCACK;
864 		m.h.magic = MSGMAGIC;
865 		len += 4;
866 		put2byte(m.h.size, len);
867 		len += sizeof(struct mesg);
868 		if(iwrite(net, &m, len) != len)
869 			return;
870 	}
871 }
872 
873 /*
874  *  Read the keyboard, convert to mesgld messages, and write it to the network.
875  *  '^\' gets us into the menu.
876  */
877 void
878 msgfromkbd(int net)
879 {
880 	long n;
881 	char buf[MAXMSG];
882 
883 	for(;;){
884 		n = iread(0, buf, sizeof(buf));
885 		if(n < 0)
886 			return;
887 		if(n && memchr(buf, 0034, n)){
888 			if(menu(net) < 0)
889 				return;
890 		} else {
891 			if(msgwrite(net, buf, n) != n)
892 				return;
893 		}
894 	}
895 }
896 
897 int
898 msgwrite(int fd, void *buf, int len)
899 {
900 	Msg m;
901 	int n;
902 
903 	n = len;
904 	memmove(m.b, buf, n);
905 	put2byte(m.h.size, n);
906 	m.h.magic = MSGMAGIC;
907 	m.h.type = M_DATA;
908 	n += sizeof(struct mesg);
909 	if(iwrite(fd, &m, n) != n)
910 		return -1;
911 
912 	put2byte(m.h.size, 0);
913 	m.h.magic = MSGMAGIC;
914 	m.h.type = M_DELIM;
915 	n = sizeof(struct mesg);
916 	if(iwrite(fd, &m, n) != n)
917 		return -1;
918 
919 	return len;
920 }
921 
922