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