xref: /illumos-gate/usr/src/cmd/vntsd/vntsdvcc.c (revision 7636cb21)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Configuration and setup interface to vcc driver.
29  * At intialization time, vntsd opens vcc ctrl port and read initial
30  * configuratioa. It manages console groups, creates the listen thread,
31  * dynamically adds and removes virtual console within a group.
32  */
33 
34 
35 #include <syslog.h>
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/ipc.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <sys/ipc.h>
44 #include <sys/shm.h>
45 #include <sys/sem.h>
46 #include <wait.h>
47 #include <time.h>
48 #include <synch.h>
49 #include <netinet/in.h>
50 #include <thread.h>
51 #include <signal.h>
52 #include "vntsd.h"
53 
54 /* signal all clients that console has been deleted */
55 boolean_t
56 vntsd_notify_client_cons_del(vntsd_client_t *clientp)
57 {
58 	(void) mutex_lock(&clientp->lock);
59 	clientp->status |= VNTSD_CLIENT_CONS_DELETED;
60 	(void) thr_kill(clientp->cons_tid, SIGUSR1);
61 	(void) mutex_unlock(&clientp->lock);
62 	return (B_FALSE);
63 }
64 
65 /* free console  structure */
66 static void
67 free_cons(vntsd_cons_t *consp)
68 {
69 	assert(consp);
70 	(void) mutex_destroy(&consp->lock);
71 	(void) cond_destroy(&consp->cvp);
72 	free(consp);
73 }
74 
75 /*
76  *  all clients connected to a console must disconnect before
77  *  removing a console.
78  */
79 static void
80 cleanup_cons(vntsd_cons_t *consp)
81 {
82 	vntsd_group_t	*groupp;
83 	timestruc_t	to;
84 
85 	assert(consp);
86 	D1(stderr, "t@%d vntsd_disconn_clients@%d\n", thr_self(),
87 	    consp->cons_no);
88 
89 	groupp = consp->group;
90 	assert(groupp);
91 
92 
93 	(void) mutex_lock(&consp->lock);
94 
95 	/* wait for all clients disconnect from the console */
96 	while (consp->clientpq != NULL) {
97 		consp->status |= VNTSD_CONS_SIG_WAIT;
98 
99 		/* signal client to disconnect the console */
100 		(void) vntsd_que_walk(consp->clientpq,
101 		    (el_func_t)vntsd_notify_client_cons_del);
102 
103 		(void) thr_kill(consp->wr_tid, SIGUSR1);
104 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
105 		to.tv_nsec = 0;
106 
107 		/* wait for clients to disconnect  */
108 		(void) cond_reltimedwait(&consp->cvp, &consp->lock, &to);
109 	}
110 
111 	(void) mutex_unlock(&consp->lock);
112 
113 	free_cons(consp);
114 }
115 
116 /* search for a group whose console is being deleted */
117 static boolean_t
118 find_clean_cons_group(vntsd_group_t *groupp)
119 {
120 	if (groupp->status & VNTSD_GROUP_CLEAN_CONS) {
121 		return (B_TRUE);
122 	} else {
123 		return (B_FALSE);
124 	}
125 }
126 
127 /* search for a console that is being deleted */
128 static boolean_t
129 find_clean_cons(vntsd_cons_t *consp)
130 {
131 	if (consp->status & VNTSD_CONS_DELETED) {
132 		return (B_TRUE);
133 	} else {
134 		return (B_FALSE);
135 	}
136 }
137 
138 /* delete a console */
139 void
140 vntsd_delete_cons(vntsd_t *vntsdp)
141 {
142 	vntsd_group_t *groupp;
143 	vntsd_cons_t *consp;
144 
145 	for (; ; ) {
146 		/* get the group contains deleted console */
147 		(void) mutex_lock(&vntsdp->lock);
148 		groupp = vntsd_que_walk(vntsdp->grouppq,
149 		    (el_func_t)find_clean_cons_group);
150 		if (groupp == NULL) {
151 			/* no more group has console deleted */
152 			(void) mutex_unlock(&vntsdp->lock);
153 			return;
154 		}
155 		(void) mutex_lock(&groupp->lock);
156 		groupp->status &= ~VNTSD_GROUP_CLEAN_CONS;
157 		(void) mutex_unlock(&groupp->lock);
158 		(void) mutex_unlock(&vntsdp->lock);
159 
160 		for (; ; ) {
161 			/* get the console to be deleted */
162 			(void) mutex_lock(&groupp->lock);
163 			assert(groupp->conspq);
164 			consp = vntsd_que_walk(groupp->conspq,
165 			    (el_func_t)find_clean_cons);
166 			if (consp == NULL) {
167 				/* no more cons to delete */
168 				(void) mutex_unlock(&groupp->lock);
169 				break;
170 			}
171 
172 			/* remove console from the group */
173 			(void) vntsd_que_rm(&groupp->conspq, consp);
174 			groupp->num_cons--;
175 			(void) mutex_unlock(&groupp->lock);
176 
177 			/* clean up the console */
178 			cleanup_cons(consp);
179 
180 			/* delete group? */
181 			if (groupp->num_cons == 0) {
182 				/* no more console delete it */
183 				assert(groupp->vntsd);
184 
185 				(void) mutex_lock(&groupp->vntsd->lock);
186 				(void) vntsd_que_rm(&groupp->vntsd->grouppq,
187 						    groupp);
188 				(void) mutex_unlock(&groupp->vntsd->lock);
189 
190 				/* clean up the group */
191 				vntsd_clean_group(groupp);
192 				break;
193 			}
194 		}
195 	}
196 }
197 
198 /* clean up a group */
199 void
200 vntsd_clean_group(vntsd_group_t *groupp)
201 {
202 
203 	timestruc_t	to;
204 
205 	D1(stderr, "t@%d clean_group() group=%s tcp=%lld\n", thr_self(),
206 	    groupp->group_name, groupp->tcp_port);
207 
208 	(void) mutex_lock(&groupp->lock);
209 
210 	/* prevent from reentry */
211 	if (groupp->status & VNTSD_GROUP_CLEANUP) {
212 		if (groupp->listen_tid == thr_self()) {
213 			/* signal that the listen thread is exiting */
214 			groupp->status &= ~VNTSD_GROUP_SIG_WAIT;
215 			(void) cond_signal(&groupp->cvp);
216 		}
217 		(void) mutex_unlock(&groupp->lock);
218 		return;
219 	}
220 	groupp->status |= VNTSD_GROUP_CLEANUP;
221 	vntsd_free_que(&groupp->conspq, (clean_func_t)cleanup_cons);
222 	(void) mutex_unlock(&groupp->lock);
223 
224 	/* walk through no cons client queue */
225 	while (groupp->no_cons_clientpq != NULL) {
226 		groupp->status |= VNTSD_GROUP_SIG_WAIT;
227 		(void) vntsd_que_walk(groupp->no_cons_clientpq,
228 		    (el_func_t)vntsd_notify_client_cons_del);
229 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
230 		to.tv_nsec = 0;
231 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
232 	}
233 
234 	if (groupp->listen_tid == thr_self()) {
235 		/* listen thread is exiting */
236 		(void) mutex_lock(&(groupp->vntsd->lock));
237 		(void) vntsd_que_rm(&groupp->vntsd->grouppq, groupp);
238 		(void) mutex_unlock(&groupp->vntsd->lock);
239 
240 		(void) cond_destroy(&groupp->cvp);
241 		(void) mutex_unlock(&groupp->lock);
242 		(void) mutex_destroy(&groupp->lock);
243 		free(groupp);
244 		return;
245 	}
246 
247 	/* signal listen thread to exit  */
248 	groupp->status |= VNTSD_GROUP_SIG_WAIT;
249 
250 	while (groupp->status & VNTSD_GROUP_SIG_WAIT) {
251 		(void) thr_kill(groupp->listen_tid, SIGUSR1);
252 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
253 		to.tv_nsec = 0;
254 		/* wait listen thread to exit  */
255 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
256 	}
257 
258 	(void) mutex_unlock(&groupp->lock);
259 	(void) thr_join(groupp->listen_tid, NULL, NULL);
260 	/* free group */
261 	(void) cond_destroy(&groupp->cvp);
262 	(void) mutex_destroy(&groupp->lock);
263 	free(groupp);
264 }
265 
266 /* allocate and initialize console structure */
267 static vntsd_cons_t *
268 alloc_cons(vntsd_group_t *groupp, vcc_console_t *consolep)
269 {
270 	vntsd_cons_t *consp;
271 	int	rv;
272 
273 	/* allocate console */
274 	consp = (vntsd_cons_t *)malloc(sizeof (vntsd_cons_t));
275 	if (consp == NULL) {
276 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_cons");
277 		return (NULL);
278 	}
279 
280 	/* intialize console */
281 	bzero(consp, sizeof (vntsd_cons_t));
282 
283 	(void) mutex_init(&consp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
284 	(void) cond_init(&consp->cvp, USYNC_THREAD, NULL);
285 
286 	consp->cons_no = consolep->cons_no;
287 	(void) strlcpy(consp->domain_name, consolep->domain_name, MAXPATHLEN);
288 	(void) strlcpy(consp->dev_name, consolep->dev_name, MAXPATHLEN);
289 	consp->wr_tid = (thread_t)-1;
290 	consp->vcc_fd = (thread_t)-1;
291 
292 	/* join the group */
293 	(void) mutex_lock(&groupp->lock);
294 
295 	if ((rv = vntsd_que_append(&groupp->conspq, consp)) !=
296 	    VNTSD_SUCCESS) {
297 		(void) mutex_unlock(&groupp->lock);
298 		vntsd_log(rv, "alloc_cons");
299 		free_cons(consp);
300 		return (NULL);
301 	}
302 	groupp->num_cons++;
303 	consp->group = groupp;
304 
305 	(void) mutex_unlock(&groupp->lock);
306 
307 	D1(stderr, "t@%d alloc_cons@%d %s %s\n", thr_self(),
308 	    consp->cons_no, consp->domain_name, consp->dev_name);
309 
310 	return (consp);
311 }
312 
313 /* compare tcp with group->tcp */
314 static boolean_t
315 grp_by_tcp(vntsd_group_t *groupp, uint64_t *tcp_port)
316 {
317 	assert(groupp);
318 	assert(tcp_port);
319 	return (groupp->tcp_port == *tcp_port);
320 }
321 
322 /* allocate and initialize group */
323 static vntsd_group_t *
324 alloc_group(vntsd_t *vntsdp, char *group_name, uint64_t tcp_port)
325 {
326 	vntsd_group_t *groupp;
327 
328 	/* allocate group */
329 	groupp = (vntsd_group_t *)malloc(sizeof (vntsd_group_t));
330 	if (groupp == NULL) {
331 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_group");
332 		return (NULL);
333 	}
334 
335 	/* initialize group */
336 	bzero(groupp, sizeof (vntsd_group_t));
337 
338 	(void) mutex_init(&groupp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
339 	(void) cond_init(&groupp->cvp, USYNC_THREAD, NULL);
340 
341 	if (group_name != NULL) {
342 		(void) memcpy(groupp->group_name, group_name, MAXPATHLEN);
343 	}
344 
345 	groupp->tcp_port = tcp_port;
346 	groupp->listen_tid = (thread_t)-1;
347 	groupp->sockfd = (thread_t)-1;
348 	groupp->vntsd = vntsdp;
349 
350 	D1(stderr, "t@%d alloc_group@%lld:%s\n", thr_self(), groupp->tcp_port,
351 	    groupp->group_name);
352 
353 	return (groupp);
354 }
355 
356 /*
357  * Initialize a console, if console is associated with with a
358  * new group, intialize the group.
359  */
360 static int
361 alloc_cons_with_group(vntsd_t *vntsdp, vcc_console_t *consp,
362     vntsd_group_t **new_groupp)
363 {
364 	vntsd_group_t	*groupp = NULL;
365 	int		rv;
366 
367 	*new_groupp = NULL;
368 
369 	/* match group by tcp port */
370 
371 
372 	(void) mutex_lock(&vntsdp->lock);
373 	groupp = vntsd_que_find(vntsdp->grouppq,
374 	    (compare_func_t)grp_by_tcp, (void *)&(consp->tcp_port));
375 	(void) mutex_unlock(&vntsdp->lock);
376 
377 	if (groupp != NULL) {
378 		/* group with same tcp port found */
379 
380 		if (strcmp(groupp->group_name, consp->group_name)) {
381 			/* conflict group name */
382 			vntsd_log(VNTSD_ERR_VCC_GRP_NAME,
383 			    "group name is different from existing group");
384 			return (VNTSD_ERR_VCC_CTRL_DATA);
385 		}
386 
387 	} else {
388 		/* new group */
389 		groupp = alloc_group(vntsdp, consp->group_name,
390 		    consp->tcp_port);
391 		if (groupp == NULL) {
392 			return (VNTSD_ERR_NO_MEM);
393 		}
394 
395 		assert(groupp->conspq == NULL);
396 		/* queue group to vntsdp */
397 		(void) mutex_lock(&vntsdp->lock);
398 		rv = vntsd_que_append(&vntsdp->grouppq, groupp);
399 		(void) mutex_unlock(&vntsdp->lock);
400 
401 		if (rv != VNTSD_SUCCESS) {
402 			return (rv);
403 		}
404 
405 		*new_groupp = groupp;
406 	}
407 
408 	/* intialize console */
409 	if (alloc_cons(groupp, consp) == NULL) {
410 		/* no memory */
411 		if (new_groupp != NULL) {
412 			/* clean up new group */
413 			(void) cond_destroy(&groupp->cvp);
414 			(void) mutex_destroy(&groupp->lock);
415 			free(groupp);
416 		}
417 
418 		return (VNTSD_ERR_NO_MEM);
419 	}
420 
421 	return (VNTSD_SUCCESS);
422 
423 }
424 
425 
426 /* create listen thread */
427 static boolean_t
428 create_listen_thread(vntsd_group_t *groupp)
429 {
430 
431 	char err_msg[VNTSD_LINE_LEN];
432 	int rv;
433 
434 	assert(groupp);
435 
436 	(void) mutex_lock(&groupp->lock);
437 	assert(groupp->num_cons);
438 
439 	D1(stderr, "t@%d create_listen:%lld\n", thr_self(), groupp->tcp_port);
440 
441 	if ((rv = thr_create(NULL, 0, (thr_func_t)vntsd_listen_thread,
442 			    (void *)groupp, THR_DETACHED, &groupp->listen_tid))
443 	    != 0) {
444 		(void) (void) snprintf(err_msg, sizeof (err_msg),
445 		    "Can not create listen thread for"
446 		    "group %s tcp %llx\n", groupp->group_name,
447 		    groupp->tcp_port);
448 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
449 
450 		/* clean up group queue */
451 		vntsd_free_que(&groupp->conspq, (clean_func_t)free_cons);
452 		groupp->listen_tid = (thread_t)-1;
453 	}
454 
455 	(void) mutex_unlock(&groupp->lock);
456 
457 	return (rv != 0);
458 }
459 
460 /* delete a console if the console exists in the vntsd */
461 static void
462 delete_cons_before_add(vntsd_t *vntsdp, uint64_t tcp_port, uint_t cons_no)
463 {
464 	vntsd_group_t	    *groupp;
465 	vntsd_cons_t	    *consp;
466 
467 	/* group exists? */
468 	(void) mutex_lock(&vntsdp->lock);
469 	groupp = vntsd_que_find(vntsdp->grouppq, (compare_func_t)grp_by_tcp,
470 	    (void *)&(tcp_port));
471 	(void) mutex_unlock(&vntsdp->lock);
472 
473 	if (groupp == NULL) {
474 		/* no such group */
475 		return;
476 	}
477 
478 	/* group exists, if console exists? */
479 	(void) mutex_lock(&groupp->lock);
480 	consp = vntsd_que_find(groupp->conspq,
481 	    (compare_func_t)vntsd_cons_by_consno, &cons_no);
482 
483 	if (consp == NULL) {
484 		/* no such console */
485 		(void) mutex_unlock(&groupp->lock);
486 		return;
487 	}
488 	/* console exists - delete console */
489 
490 	(void) mutex_lock(&consp->lock);
491 
492 	consp->status |= VNTSD_CONS_DELETED;
493 	groupp->status |= VNTSD_GROUP_CLEAN_CONS;
494 
495 	(void) mutex_unlock(&consp->lock);
496 
497 	(void) mutex_unlock(&groupp->lock);
498 
499 	vntsd_delete_cons(vntsdp);
500 }
501 
502 /* add a console */
503 static void
504 do_add_cons(vntsd_t *vntsdp, int cons_no)
505 {
506 	vcc_console_t	console;
507 	vntsd_group_t	*groupp;
508 	int		rv;
509 	char		err_msg[VNTSD_LINE_LEN];
510 
511 
512 	(void) snprintf(err_msg, sizeof (err_msg),
513 	    "do_add_cons():Can not add console=%d", cons_no);
514 
515 	/* get console configuration from vcc */
516 
517 	if ((rv = vntsd_vcc_ioctl(VCC_CONS_INFO, cons_no, (void *)&console))
518 	    != VNTSD_SUCCESS) {
519 		vntsd_log(rv, err_msg);
520 		return;
521 	}
522 
523 	/* clean up the console if console was deleted and added again */
524 	delete_cons_before_add(vntsdp, console.tcp_port, console.cons_no);
525 
526 	/* initialize console */
527 
528 	if ((rv = alloc_cons_with_group(vntsdp, &console, &groupp)) !=
529 	    VNTSD_SUCCESS) {
530 		/* no memory to add this new console */
531 		vntsd_log(rv, err_msg);
532 		return;
533 	}
534 
535 	if (groupp != NULL) {
536 		/* new group */
537 		/* create listen thread for this console */
538 		if (create_listen_thread(groupp)) {
539 			vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
540 			(void) cond_destroy(&groupp->cvp);
541 			(void) mutex_destroy(&groupp->lock);
542 			free(groupp);
543 		}
544 
545 	}
546 }
547 
548 /* daemon wake up */
549 void
550 vntsd_daemon_wakeup(vntsd_t *vntsdp)
551 {
552 
553 	vcc_response_t	inq_data;
554 
555 	/* reason to wake up  */
556 	if (vntsd_vcc_ioctl(VCC_INQUIRY, 0, (void *)&inq_data) !=
557 	    VNTSD_SUCCESS) {
558 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "vntsd_daemon_wakeup()");
559 		return;
560 	}
561 
562 	D1(stderr, "t@%d vntsd_daemon_wakup:msg %d port %x\n", thr_self(),
563 	    inq_data.reason, inq_data.cons_no);
564 
565 	switch (inq_data.reason) {
566 
567 	case VCC_CONS_ADDED:
568 		do_add_cons(vntsdp, inq_data.cons_no);
569 		break;
570 
571 	case VCC_CONS_MISS_ADDED:
572 		/* an added port was deleted before vntsd can process it */
573 		return;
574 
575 	default:
576 		DERR(stderr, "t@%d daemon_wakeup:ioctl_unknown %d\n",
577 		    thr_self(), inq_data.reason);
578 		vntsd_log(VNTSD_ERR_UNKNOWN_CMD, "from vcc\n");
579 		break;
580 	}
581 }
582 
583 /* initial console configuration */
584 void
585 vntsd_get_config(vntsd_t *vntsdp)
586 {
587 
588 	int		i;
589 	int		num_cons;
590 	vcc_console_t	*consp;
591 	vntsd_group_t	*groupp;
592 
593 	/* num of consoles */
594 	num_cons = 0;
595 
596 	if (vntsd_vcc_ioctl(VCC_NUM_CONSOLE, 0, (void *)&num_cons) !=
597 	    VNTSD_SUCCESS) {
598 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "VCC_NUM_CONSOLE failed\n");
599 		return;
600 	}
601 
602 	D3(stderr, "get_config:num_cons=%d", num_cons);
603 
604 	if (num_cons == 0) {
605 		return;
606 	}
607 
608 	/* allocate memory for all consoles */
609 	consp = malloc(num_cons*sizeof (vcc_console_t));
610 
611 	if (consp == NULL) {
612 		vntsd_log(VNTSD_ERR_NO_MEM, "for console table.");
613 		return;
614 	}
615 
616 	/* get console table */
617 	if (vntsd_vcc_ioctl(VCC_CONS_TBL, 0, (void *)consp) != VNTSD_SUCCESS) {
618 		vntsd_log(VNTSD_ERR_VCC_IOCTL, " VCC_CONS_TBL "
619 		    "for console table\n");
620 		return;
621 	}
622 
623 	/* intialize groups and consoles  */
624 	for (i = 0; i < num_cons; i++) {
625 		if (alloc_cons_with_group(vntsdp, &consp[i], &groupp)
626 		    != VNTSD_SUCCESS) {
627 			vntsd_log(VNTSD_ERR_ADD_CONS_FAILED, "get_config");
628 		}
629 	}
630 
631 	/* create listen thread for each group */
632 	(void) mutex_lock(&vntsdp->lock);
633 
634 	for (; ; ) {
635 		groupp = vntsd_que_walk(vntsdp->grouppq,
636 		    (el_func_t)create_listen_thread);
637 		if (groupp == NULL) {
638 			break;
639 		}
640 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, "get config()");
641 	}
642 
643 	(void) mutex_unlock(&vntsdp->lock);
644 }
645