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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*
26 * poolbind - bind processes, tasks, and projects to pools, and query process
27 * pool bindings
28 */
29
30#include <libgen.h>
31#include <pool.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <strings.h>
36#include <unistd.h>
37
38#include <locale.h>
39#include <libintl.h>
40
41#include <sys/procset.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <errno.h>
45#include <project.h>
46#include <zone.h>
47
48#include "utils.h"
49
50#ifndef	TEXT_DOMAIN
51#define	TEXT_DOMAIN	"SYS_TEST"
52#endif
53
54#define	eFLAG	0x1
55#define	iFLAG	0x2
56#define	pFLAG	0x4
57#define	qFLAG	0x8
58#define	QFLAG	0x10
59
60static const char OPTS[] = "Qei:p:q";
61static struct {
62	idtype_t idtype;
63	char *str;
64} idtypes[] = {
65	{ P_PID, "pid" },
66	{ P_TASKID, "taskid" },
67	{ P_PROJID, "projid" },
68	{ P_PROJID, "project" },
69	{ P_ZONEID, "zoneid" },
70	{ -1, NULL }
71};
72
73int error = E_PO_SUCCESS;
74
75void exec_cmd(char *, char *[]);
76void process_ids(char *, uint_t, idtype_t, char *, int, char *[]);
77
78void
79usage(void)
80{
81	(void) fprintf(stderr,
82	    gettext("Usage:\n"
83	    "    poolbind -p pool_name -e command [arguments...]\n"
84	    "    poolbind -p pool_name "
85	    "[-i pid | -i taskid | -i projid | -i zoneid] id ...\n"
86	    "    poolbind -q pid ...\n"
87	    "    poolbind -Q pid ... \n"));
88	exit(E_USAGE);
89}
90
91int
92print_resource_binding(const char *type, pid_t pid)
93{
94	char *resource_name;
95
96	if ((resource_name = pool_get_resource_binding(type, pid)) == NULL)
97		warn(gettext("getting '%s' binding for %d: %s\n"), type,
98		    (int)pid, get_errstr());
99	else
100		(void) printf("%d\t%s\t%s\n", (int)pid, type, resource_name);
101	free(resource_name);
102	return (PO_SUCCESS);
103}
104
105int
106main(int argc, char *argv[])
107{
108	char c;
109	int i;
110	idtype_t idtype = P_PID;
111	char *idstr = "pid";
112	char *pool_name = NULL;
113	uint_t flags = 0;
114	int status;
115
116	(void) getpname(argv[0]);
117	(void) setlocale(LC_ALL, "");
118	(void) textdomain(TEXT_DOMAIN);
119
120	while ((c = getopt(argc, argv, OPTS)) != EOF) {
121		switch (c) {
122		case 'Q':
123			if (flags & (qFLAG | iFLAG | pFLAG))
124				usage();
125			flags |= QFLAG;
126			break;
127		case 'e':
128			if (flags & (iFLAG | qFLAG | QFLAG))
129				usage();
130			flags |= eFLAG;
131			break;
132		case 'i':
133			for (i = 0; idtypes[i].str != NULL; i++) {
134				if (strcmp(optarg, idtypes[i].str) == 0) {
135					idtype = idtypes[i].idtype;
136					idstr = idtypes[i].str;
137					break;
138				}
139			}
140			if ((flags & (iFLAG | qFLAG | QFLAG)) ||
141			    idtypes[i].str == NULL)
142				usage();
143			flags |= iFLAG;
144			break;
145		case 'p':
146			if (flags & (pFLAG | qFLAG | QFLAG))
147				usage();
148			flags |= pFLAG;
149			pool_name = optarg;
150			break;
151		case 'q':
152			if (flags & (pFLAG | iFLAG | QFLAG))
153				usage();
154			flags |= qFLAG;
155			break;
156		case '?':
157		default:
158			usage();
159		}
160	}
161
162	argc -= optind;
163	argv += optind;
164
165	if (flags & eFLAG && pool_name == NULL)
166		usage();
167	if (argc < 1 || (flags & (pFLAG | qFLAG | QFLAG)) == 0)
168		usage();
169
170	/*
171	 * Check to see that the pools facility is enabled
172	 */
173	if (pool_get_status(&status) != PO_SUCCESS)
174		die((ERR_OPEN_DYNAMIC), get_errstr());
175	if (status == POOL_DISABLED)
176		die((ERR_OPEN_DYNAMIC), strerror(ENOTACTIVE));
177
178	if (flags & eFLAG)
179		exec_cmd(pool_name, argv);
180		/*NOTREACHED*/
181	else
182		process_ids(pool_name, flags, idtype, idstr, argc, argv);
183
184	return (error);
185}
186
187void
188exec_cmd(char *pool_name, char *argv[])
189{
190	if (pool_set_binding(pool_name, P_PID, getpid()) != PO_SUCCESS) {
191		warn(gettext("binding to pool '%s': %s\n"), pool_name,
192		    get_errstr());
193		error = E_ERROR;
194		return;
195	}
196
197	if (execvp(argv[0], argv) == -1)
198		die(gettext("exec of %s failed"), argv[0]);
199	/*NOTREACHED*/
200}
201
202void
203process_ids(char *pool_name, uint_t flags, idtype_t idtype, char *idstr,
204    int argc, char *argv[])
205{
206	int i;
207	id_t id;
208
209	for (i = 0; i < argc; i++) {
210		char *endp;
211		char *poolname;
212
213		errno = 0;
214		id = (id_t)strtol(argv[i], &endp, 10);
215		if (errno != 0 ||
216		    (endp && endp != argv[i] + strlen(argv[i])) ||
217		    (idtype == P_ZONEID &&
218		    getzonenamebyid(id, NULL, 0) == -1)) {
219			/*
220			 * The string does not completely parse to
221			 * an integer, or it represents an invalid
222			 * zone id.
223			 */
224
225			/*
226			 * It must be a project or zone name.
227			 */
228			if (idtype == P_ZONEID) {
229				if (zone_get_id(argv[i], &id) != 0) {
230					warn(gettext("invalid zone '%s'\n"),
231					    argv[i]);
232					error = E_ERROR;
233					continue;
234				}
235				/* make sure the zone is booted */
236				if (id == -1) {
237					warn(gettext("zone '%s' is not "
238					    "active\n"), argv[i]);
239					error = E_ERROR;
240					continue;
241				}
242			} else if (idtype == P_PROJID) {
243				if ((id = getprojidbyname(argv[i])) < 0) {
244					warn(gettext("failed to get project "
245					    "id for project: '%s'"), argv[i]);
246					error = E_ERROR;
247					continue;
248				}
249			} else {
250				warn(gettext("invalid %s '%s'\n"),
251				    idstr, argv[i]);
252				error = E_ERROR;
253				continue;
254			}
255		}
256
257		if (flags & pFLAG) {
258			if (pool_set_binding(pool_name, idtype, id) !=
259			    PO_SUCCESS) {
260				warn(gettext("binding %s %ld to pool '%s': "
261				    "%s\n"), idstr, id, pool_name,
262				    get_errstr());
263				error = E_ERROR;
264			}
265			continue;
266		}
267
268		if (flags & qFLAG) {
269			if ((poolname = pool_get_binding(id)) == NULL) {
270				warn(gettext("couldn't determine binding for "
271				    "pid %ld: %s\n"), id, get_errstr());
272				error = E_ERROR;
273			} else {
274				(void) printf("%ld\t%s\n", id, poolname);
275				free(poolname);
276			}
277		}
278		if (flags & QFLAG) {
279			uint_t j, count;
280			const char **resource_types;
281			(void) pool_resource_type_list(NULL, &count);
282
283			if ((resource_types = malloc(count *
284			    sizeof (const char *))) == NULL) {
285				warn(gettext("couldn't allocate query memory "
286				    "for pid %ld: %s\n"), id, get_errstr());
287				error = E_ERROR;
288			}
289			(void) pool_resource_type_list(resource_types, &count);
290
291			for (j = 0; j < count; j++)
292				(void) print_resource_binding(resource_types[j],
293				    (pid_t)id);
294			free(resource_types);
295		}
296	}
297}
298