xref: /illumos-gate/usr/src/cmd/vntsd/common.c (revision ea51a530fee7b8cfb2929d89b71aa20d8d9e2118)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * supporting modules.
31  */
32 
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/ipc.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <sys/socket.h>
40 #include <sys/ipc.h>
41 #include <sys/shm.h>
42 #include <sys/sem.h>
43 #include <sys/poll.h>
44 #include <wait.h>
45 #include <time.h>
46 #include <netinet/in.h>
47 #include <thread.h>
48 #include <signal.h>
49 #include <ctype.h>
50 #include <langinfo.h>
51 #include <libintl.h>
52 #include <syslog.h>
53 #include "vntsd.h"
54 #include "chars.h"
55 
56 /*  vntsd_write_line() - write a line to TCP client */
57 int
58 vntsd_write_line(vntsd_client_t *clientp, char *line)
59 {
60 	int rv;
61 
62 	rv = vntsd_write_client(clientp, line, strlen(line));
63 	if (rv == VNTSD_SUCCESS) {
64 		rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN);
65 	}
66 
67 	return (rv);
68 }
69 
70 /*  vntsd_write_lines() write one or more lines to client.  */
71 int
72 vntsd_write_lines(vntsd_client_t *clientp, char *lines)
73 {
74 	char	*buf;
75 	char	*line;
76 	char 	*endofline;
77 
78 	buf = strdup(lines);
79 	if (buf == NULL) {
80 		return (VNTSD_ERR_NO_MEM);
81 	}
82 
83 	line = buf;
84 
85 	while ((line != NULL) && (*line != '\0')) {
86 
87 		endofline = strchr(line, '\n');
88 		if (endofline != NULL) {
89 			*endofline = '\0';
90 		}
91 
92 		(void) vntsd_write_line(clientp, line);
93 
94 		if (endofline != NULL)
95 			line = endofline + 1;
96 		else
97 			line = NULL;
98 	}
99 
100 	free(buf);
101 	return (VNTSD_SUCCESS);
102 }
103 
104 /* vntsd_get_yes_no() -  read in a "y" or "n" */
105 int
106 vntsd_get_yes_no(vntsd_client_t *clientp, char *msg, int *yes_no)
107 {
108 	char	c;
109 	char	yesno[8];
110 	int	rv;
111 
112 	/* create [y/n] prompt */
113 	(void) snprintf(yesno, sizeof (yesno), "[%c/%c] ",
114 	    *nl_langinfo(YESSTR), *nl_langinfo(NOSTR));
115 
116 	for (; ; ) {
117 		if ((rv = vntsd_write_client(clientp, msg, strlen(msg)))
118 		    != VNTSD_SUCCESS) {
119 			return (rv);
120 		}
121 
122 		if ((rv = vntsd_write_client(clientp, yesno, strlen(yesno))) !=
123 		    VNTSD_SUCCESS) {
124 			return (rv);
125 		}
126 
127 		if ((rv = vntsd_read_data(clientp, &c))
128 		    != VNTSD_SUCCESS) {
129 			return (rv);
130 		}
131 
132 		/* echo */
133 		if ((rv = vntsd_write_client(clientp, &c, 1)) !=
134 		    VNTSD_SUCCESS) {
135 			return (rv);
136 		}
137 
138 		if ((rv = vntsd_write_client(clientp, vntsd_eol,
139 			VNTSD_EOL_LEN)) !=
140 		    VNTSD_SUCCESS) {
141 			return (rv);
142 		}
143 
144 		c = tolower(c);
145 
146 		if (c == *nl_langinfo(YESSTR)) {
147 			*yes_no = B_TRUE;
148 			return (VNTSD_SUCCESS);
149 		}
150 
151 		if (c == *nl_langinfo(NOSTR)) {
152 			*yes_no = B_FALSE;
153 			return (VNTSD_SUCCESS);
154 		}
155 
156 		if ((rv = vntsd_write_line(clientp,
157 		    gettext("Invalid response. Try again.")))
158 		    != VNTSD_SUCCESS) {
159 			return (rv);
160 		}
161 	}
162 
163 	/*NOTREACHED*/
164 	return (0);
165 }
166 
167 /* vntsd_open_vcc()  -  open a vcc port */
168 int
169 vntsd_open_vcc(char *dev_name, uint_t cons_no)
170 {
171 	int	drvfd;
172 	int	sz;
173 	char	*path;
174 	sz = strlen(VCC_DEVICE_PATH) + strlen(dev_name)+1;
175 
176 	path = calloc(sz, 1);
177 
178 	if (path == NULL) {
179 		return (-1);
180 	}
181 
182 	(void) snprintf(path, sz-1, VCC_DEVICE_PATH, dev_name);
183 
184 	for (; ; ) {
185 		drvfd = open(path, O_RDWR);
186 
187 		if ((drvfd < 0) && (errno == EAGAIN)) {
188 			if (vntsd_vcc_ioctl(VCC_FORCE_CLOSE, cons_no, &cons_no)
189 				!= VNTSD_SUCCESS) {
190 				break;
191 			    }
192 		} else {
193 			break;
194 		}
195 	}
196 
197 
198 	if (drvfd < 0) {
199 		D1(stderr, "t@%d open_vcc@%s exit\n", thr_self(), dev_name);
200 		free(path);
201 		return (-1);
202 	}
203 
204 	free(path);
205 	return (drvfd);
206 }
207 
208 /* vntsd_cons_by_consno() - match a console structure to cons no */
209 boolean_t
210 vntsd_cons_by_consno(vntsd_cons_t *consp, int *cons_id)
211 {
212 	if (consp->status & VNTSD_CONS_DELETED) {
213 		return (B_FALSE);
214 	}
215 	return (consp->cons_no == *cons_id);
216 }
217 
218 /* vntsd_write_client() write to telnet client */
219 int
220 vntsd_write_client(vntsd_client_t *client, char *buffer, size_t sz)
221 {
222 	int rv;
223 
224 
225 	/* write to client */
226 	rv = vntsd_write_fd(client->sockfd, buffer, sz);
227 
228 	/* client has output, reset timer */
229 	vntsd_reset_timer(client->cons_tid);
230 
231 	return (rv);
232 }
233 
234 /* vntsd_write_fd() write to tcp socket file descriptor  */
235 int
236 vntsd_write_fd(int fd, void *buf, size_t sz)
237 {
238 	int n;
239 
240 	while (sz > 0) {
241 		n = write(fd, buf, sz);
242 		if (n < 0) {
243 			if (errno == EINTR) {
244 				return (VNTSD_STATUS_INTR);
245 			}
246 
247 			return (VNTSD_STATUS_CLIENT_QUIT);
248 		}
249 
250 		if (n == 0) {
251 			return (VNTSD_STATUS_CLIENT_QUIT);
252 		}
253 
254 		buf =  (caddr_t)buf + n;
255 		sz -= n;
256 	}
257 	return (VNTSD_SUCCESS);
258 
259 }
260 
261 /*
262  * vntsd_read_char() - read a char from TCP Clienti. Returns:
263  * VNTSD_SUCCESS, VNTSD_STATUS_CLIENT_QUIT or VNTSD_STATUS_INTR
264  */
265 int
266 vntsd_read_char(vntsd_client_t *clientp, char *c)
267 {
268 	int		n;
269 	vntsd_timeout_t tmo;
270 	int		rv;
271 
272 	tmo.tid = thr_self();
273 	tmo.minutes = 0;
274 	tmo.clientp = clientp;
275 
276 	/* attach to timer */
277 	if ((rv = vntsd_attach_timer(&tmo)) != VNTSD_SUCCESS) {
278 		return (rv);
279 	}
280 
281 	n = read(clientp->sockfd, c, 1);
282 
283 	/* detach from timer */
284 	if ((rv = vntsd_detach_timer(&tmo)) != VNTSD_SUCCESS) {
285 		return (rv);
286 	}
287 
288 	if (n == 1) {
289 		return (VNTSD_SUCCESS);
290 	}
291 
292 	if (n == 0) {
293 		return (VNTSD_STATUS_CLIENT_QUIT);
294 	}
295 
296 	/*
297 	 * read error or wake up by signal, either console is being removed or
298 	 * timeout occurs.
299 	 */
300 	if (errno == EINTR) {
301 		return (VNTSD_STATUS_INTR);
302 	}
303 
304 	/* any other error, we close client */
305 	return (VNTSD_STATUS_CLIENT_QUIT);
306 }
307 
308 /*
309  * vntsd_read_data() -  handle special commands
310  * such as telnet, daemon and ctrl cmds. Returns:
311  * from vntsd_read_char:
312  *	    VNTSD_STATUS_CLIENT_QUIT
313  *	    VNTSD_STATUS_INTR
314  * from vnts_process_daemon_cmd:
315  *	    VNTSD_STATUS_RESELECT_CONS
316  *	    VNTSD_STATUS_MOV_CONS_FORWARD
317  *	    VNTSD_STATUS_MOV_CONS_BACKWARD
318  *	    VNTSD_STATUS_ACQURE_WRITER
319  *	    VNTSD_STATUS_CONTINUE
320  * from vntsd_telnet_cmd
321  *	    VNTSD_STATUS_CONTINUE
322  */
323 int
324 vntsd_read_data(vntsd_client_t *clientp, char *c)
325 {
326 	int rv;
327 
328 	for (; ; ) {
329 		if ((rv = vntsd_read_char(clientp, c)) != VNTSD_SUCCESS) {
330 		    return (rv);
331 		}
332 
333 		/* daemon cmd? */
334 		rv = vntsd_process_daemon_cmd(clientp, *c);
335 
336 		if (rv == VNTSD_SUCCESS) {
337 			/* telnet cmd? */
338 			rv = vntsd_telnet_cmd(clientp, *c);
339 		}
340 
341 		if (rv == VNTSD_STATUS_CONTINUE) {
342 			/*
343 			 * either a daemon cmd or a telnet cmd
344 			 * was processed.
345 			 */
346 			clientp->prev_char = 0;
347 			continue;
348 		}
349 
350 		return (rv);
351 	}
352 
353 	/*NOTREACHED*/
354 	return (0);
355 }
356 /* vntsd_read_line() -  read a line from TCP client */
357 int
358 vntsd_read_line(vntsd_client_t *clientp, char *buf, int *in_sz)
359 {
360 	char	c;
361 	int	rv;
362 	int	out_sz = 0;
363 
364 
365 	for (; ; ) {
366 
367 		if ((rv =  vntsd_read_data(clientp, &c)) !=  VNTSD_SUCCESS) {
368 		    return (rv);
369 		}
370 
371 		if (c == BS) {
372 			/* back */
373 			if ((rv = vntsd_write_client(clientp, &c, 1)) !=
374 			    VNTSD_SUCCESS) {
375 				return (rv);
376 			}
377 
378 			c = ' ';
379 			if ((rv = vntsd_write_client(clientp, &c, 1)) !=
380 			    VNTSD_SUCCESS) {
381 				return (rv);
382 			}
383 
384 			buf--;
385 			out_sz--;
386 			continue;
387 		}
388 		/* echo */
389 		if ((rv = vntsd_write_client(clientp, &c, 1)) !=
390 		    VNTSD_SUCCESS) {
391 			return (rv);
392 		}
393 
394 		*buf++ = c;
395 		out_sz++;
396 
397 		if (c == CR) {
398 			/* end of line */
399 			*in_sz = out_sz;
400 			return (VNTSD_SUCCESS);
401 		}
402 
403 		if (out_sz == *in_sz) {
404 			return (VNTSD_SUCCESS);
405 		}
406 	}
407 
408 	/*NOTREACHED*/
409 	return (0);
410 }
411 
412 /* free a client */
413 void
414 vntsd_free_client(vntsd_client_t *clientp)
415 {
416 
417 	if (clientp->sockfd != -1) {
418 		(void) close(clientp->sockfd);
419 	}
420 
421 	(void) mutex_destroy(&clientp->lock);
422 
423 	free(clientp);
424 }
425 
426 
427 /* check if a vcc console port still ok */
428 boolean_t
429 vntsd_vcc_cons_alive(vntsd_cons_t *consp)
430 {
431 	vcc_console_t	vcc_cons;
432 	int		rv;
433 
434 	assert(consp);
435 	assert(consp->group);
436 
437 	/* construct current configuration */
438 	(void) strncpy(vcc_cons.domain_name, consp->domain_name, MAXPATHLEN);
439 	(void) strncpy(vcc_cons.group_name, consp->group->group_name,
440 	    MAXPATHLEN);
441 	vcc_cons.tcp_port = consp->group->tcp_port;
442 	vcc_cons.cons_no   = consp->cons_no;
443 
444 	/* call vcc to verify */
445 	rv = vntsd_vcc_ioctl(VCC_CONS_STATUS, consp->cons_no, &vcc_cons);
446 	if (rv != VNTSD_SUCCESS) {
447 		return (B_FALSE);
448 	}
449 
450 	if (vcc_cons.cons_no == -1) {
451 		/* port is gone */
452 		return (B_FALSE);
453 	}
454 
455 	/* port is ok */
456 	return (B_TRUE);
457 
458 }
459 
460 /* add to total if a console is alive  */
461 static boolean_t
462 total_cons(vntsd_cons_t *consp, int *num_cons)
463 {
464 	int rv;
465 
466 	assert(consp->group);
467 	rv = vntsd_vcc_err(consp);
468 	if (rv == VNTSD_STATUS_CONTINUE) {
469 		(*num_cons)++;
470 	}
471 	return (B_FALSE);
472 }
473 
474 
475 /* total alive consoles in a group  */
476 int
477 vntsd_chk_group_total_cons(vntsd_group_t *groupp)
478 {
479 	uint_t num_cons = 0;
480 
481 	(void) vntsd_que_find(groupp->conspq, (compare_func_t)total_cons,
482 	    &num_cons);
483 	return (num_cons);
484 }
485 
486 /* vntsd_log() log function for errors */
487 void
488 vntsd_log(vntsd_status_t status, char *msg)
489 {
490 	char	*status_msg = NULL;
491 	int	critical = 0;
492 
493 	switch (status) {
494 
495 	case VNTSD_SUCCESS:
496 		status_msg = "STATUS_OK";
497 		break;
498 
499 	case VNTSD_STATUS_CONTINUE:
500 		status_msg = "CONTINUE";
501 		break;
502 
503 	case VNTSD_STATUS_EXIT_SIG:
504 		critical = 1;
505 		status_msg = "KILL SIGNAL RECV";
506 		break;
507 
508 	case VNTSD_STATUS_SIG:
509 		status_msg = "SIG RECV";
510 		break;
511 
512 	case VNTSD_STATUS_NO_HOST_NAME:
513 		status_msg = "Warining NO HOST NAME";
514 		break;
515 
516 	case VNTSD_STATUS_CLIENT_QUIT:
517 		status_msg = "CLIENT CLOSED  GROUP CONNECTION";
518 		break;
519 
520 	case VNTSD_STATUS_RESELECT_CONS:
521 		status_msg = "CLIENT RESELECTS CONSOLE";
522 		break;
523 
524 	case VNTSD_STATUS_VCC_IO_ERR:
525 		status_msg = "CONSOLE WAS DELETED";
526 		break;
527 
528 	case VNTSD_STATUS_MOV_CONS_FORWARD:
529 		status_msg = "MOVE CONSOLE FORWARD";
530 		break;
531 
532 	case VNTSD_STATUS_MOV_CONS_BACKWARD:
533 		status_msg = "MOVE CONSOLE BACKWARD";
534 		break;
535 
536 	case VNTSD_STATUS_ACQUIRE_WRITER:
537 		status_msg = "FORCE CONSOLE WRITE";
538 		break;
539 
540 	case VNTSD_STATUS_INTR:
541 		status_msg = "RECV SIGNAL";
542 		break;
543 
544 	case VNTSD_STATUS_DISCONN_CONS:
545 		status_msg = "DELETING CONSOLE";
546 		break;
547 
548 	case VNTSD_STATUS_NO_CONS:
549 		status_msg = "All console(s) in the group have been deleted.";
550 		break;
551 
552 	case VNTSD_ERR_NO_MEM:
553 		critical = 1;
554 		status_msg = "NO MEMORY";
555 		break;
556 
557 	case VNTSD_ERR_NO_DRV:
558 		critical = 1;
559 		status_msg = "NO VCC DRIVER";
560 		break;
561 
562 	case VNTSD_ERR_WRITE_CLIENT:
563 		status_msg  =  "WRITE CLIENT ERR";
564 		break;
565 
566 	case VNTSD_ERR_EL_NOT_FOUND:
567 		critical = 1;
568 		status_msg = "ELEMENT_NOT_FOUND";
569 		break;
570 
571 	case VNTSD_ERR_VCC_CTRL_DATA:
572 		critical = 1;
573 		status_msg = "VCC CTRL DATA  ERROR";
574 		break;
575 
576 	case VNTSD_ERR_VCC_POLL:
577 		critical = 1;
578 		status_msg = "VCC POLL ERROR";
579 		break;
580 
581 	case VNTSD_ERR_VCC_IOCTL:
582 		critical = 1;
583 		status_msg = "VCC IOCTL ERROR";
584 		break;
585 
586 	case VNTSD_ERR_VCC_GRP_NAME:
587 		critical = 1;
588 		status_msg = "VCC GROUP NAME ERROR";
589 		break;
590 
591 	case VNTSD_ERR_CREATE_LISTEN_THR:
592 		critical = 1;
593 		status_msg = "FAIL TO CREATE LISTEN THREAD";
594 		break;
595 
596 	case VNTSD_ERR_CREATE_WR_THR:
597 		critical = 1;
598 		status_msg = "FAIL TO CREATE WRITE THREAD";
599 		break;
600 
601 	case VNTSD_ERR_ADD_CONS_FAILED:
602 		critical = 1;
603 		status_msg = "FAIL TO ADD A CONSOLE";
604 		break;
605 
606 	case VNTSD_ERR_LISTEN_SOCKET:
607 		critical = 1;
608 		status_msg = "LISTEN SOCKET ERROR";
609 		break;
610 
611 	case VNTSD_ERR_LISTEN_OPTS:
612 		critical = 1;
613 		status_msg = "SET SOCKET OPTIONS ERROR";
614 		break;
615 
616 	case VNTSD_ERR_LISTEN_BIND:
617 		critical = 1;
618 		status_msg = "BIND SOCKET ERROR";
619 		break;
620 
621 	case VNTSD_STATUS_ACCEPT_ERR:
622 		critical = 1;
623 		status_msg = "LISTEN ACCEPT ERROR";
624 		break;
625 
626 	case VNTSD_ERR_CREATE_CONS_THR:
627 		critical = 1;
628 		status_msg = "CREATE CONSOLE THREAD ERROR ";
629 		break;
630 
631 	case VNTSD_ERR_SIG:
632 		critical = 1;
633 		status_msg = "RECV UNKNOWN SIG";
634 		break;
635 
636 	case VNTSD_ERR_UNKNOWN_CMD:
637 		critical = 1;
638 		status_msg = "RECV UNKNOWN COMMAND";
639 		break;
640 
641 	case VNTSD_ERR_CLIENT_TIMEOUT:
642 		status_msg  =  "CLOSE CLIENT BECAUSE TIMEOUT";
643 		break;
644 	default:
645 		status_msg = "Unknown status recv";
646 		break;
647 	}
648 
649 
650 	if (critical) {
651 		syslog(LOG_ERR, "%s: thread[%d] %s\n", status_msg,
652 		    thr_self(), msg);
653 	}
654 #ifdef DEBUG
655 	DERR(stderr, "%s: thread[%d] %s\n", status_msg,
656 		    thr_self(), msg);
657 	syslog(LOG_ERR, "%s: thread[%d] %s\n", status_msg, thr_self(), msg);
658 #endif
659 }
660