dd_misc.c revision 11262:b7ebfbf2359e
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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <dhcp_svc_private.h>
27#include <dirent.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdio.h>
31#include <errno.h>
32#include <signal.h>
33#include <fcntl.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/socket.h>
37#include <sys/sockio.h>
38#include <net/if.h>
39#include <libintl.h>
40#include <procfs.h>
41#include <rpcsvc/nis.h>
42#include <malloc.h>
43#include <ctype.h>
44#include <jni.h>
45
46#include "exception.h"
47#include "class_cache.h"
48#include "dd_misc.h"
49
50#define	PROCFS_DIR	"/proc"
51
52/*
53 * Frees the list of data store strings.
54 */
55static void
56free_enumerated_dd(char **module, int count)
57{
58	while (count-- > 0) {
59		free(module[count]);
60	}
61	free(module);
62}
63
64/*
65 * Determines if a given data store name is valid.
66 */
67static boolean_t
68dd_is_valid_data_store(JNIEnv *env, const char *name)
69{
70	char **module;
71	int count;
72	int ndx;
73	int rcode;
74	boolean_t isValid = B_FALSE;
75
76	if (name != NULL) {
77		rcode = enumerate_dd(&module, &count);
78		if (rcode != DSVC_SUCCESS) {
79			throw_libdhcpsvc_exception(env, rcode);
80		} else {
81			for (ndx = 0; !isValid && ndx < count; ndx++) {
82				if (strcmp(module[ndx], name) == 0) {
83					isValid = B_TRUE;
84				}
85			}
86			free_enumerated_dd(module, count);
87		}
88	}
89	return (isValid);
90}
91
92/*
93 * Call a java object's int getter method.
94 */
95static boolean_t
96dd_get_int_attr(
97	JNIEnv *env,
98	jclass class,
99	int id,
100	jobject obj,
101	int *value) {
102
103	jmethodID methodID;
104	jint jval;
105	boolean_t noException = B_TRUE;
106
107	methodID = get_methodID(env, class, id);
108	if (methodID == NULL) {
109		noException = B_FALSE;
110	} else {
111		jval = (*env)->CallIntMethod(env, obj, methodID);
112		if ((*env)->ExceptionOccurred(env) != NULL) {
113			noException = B_FALSE;
114		} else {
115			*value = jval;
116		}
117	}
118	return (noException);
119}
120
121/*
122 * Convert a possibly multi-byte string to a jstring.
123 */
124jstring
125dd_native_to_jstring(JNIEnv *env, const char *str)
126{
127	jstring result;
128	jbyteArray bytes = 0;
129	int len;
130
131	jclass strclass;
132	jmethodID mid;
133
134	strclass = (*env)->FindClass(env, "java/lang/String");
135	if (strclass == NULL) {
136		/* exception thrown */
137		return (NULL);
138	}
139
140	mid = (*env)->GetMethodID(env, strclass, "<init>", "([B)V");
141	if (mid == NULL) {
142		/* exception thrown */
143		return (NULL);
144	}
145
146	len = strlen(str);
147	bytes = (*env)->NewByteArray(env, len);
148	if (bytes == NULL) {
149		/* exception thrown */
150		return (NULL);
151	}
152
153	(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) str);
154	result = (*env)->NewObject(env, strclass, mid, bytes);
155
156	(*env)->DeleteLocalRef(env, bytes);
157	return (result);
158}
159
160/*
161 * Convert a jstring to a possibly multi-byte string.
162 */
163char *
164dd_jstring_to_native(JNIEnv *env, jstring jstr) {
165
166	jbyteArray bytes = 0;
167	jint len;
168	char *result;
169
170	jclass strclass;
171	jmethodID mid;
172
173	strclass = (*env)->FindClass(env, "java/lang/String");
174	if (strclass == NULL) {
175		/* exception thrown */
176		return (NULL);
177	}
178
179	mid = (*env)->GetMethodID(env, strclass, "getBytes", "()[B");
180	if (mid == NULL) {
181		/* exception thrown */
182		return (NULL);
183	}
184
185	bytes = (*env)->CallObjectMethod(env, jstr, mid);
186	if ((*env)->ExceptionOccurred(env)) {
187		/* exception thrown */
188		return (NULL);
189	}
190
191	len = (*env)->GetArrayLength(env, bytes);
192	result = (char *)malloc(len + 1);
193	if (result == NULL) {
194		throw_memory_exception(env);
195		(*env)->DeleteLocalRef(env, bytes);
196		return (NULL);
197	}
198
199	(*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *) result);
200	result[len] = 0;
201
202	(*env)->DeleteLocalRef(env, bytes);
203	return (result);
204}
205
206/*
207 * Convert a jstring to a UTF-8 string.
208 */
209boolean_t
210dd_jstring_to_UTF(
211	JNIEnv *env,
212	jstring javaString,
213	char **nativeString)
214{
215	boolean_t noException = B_TRUE;
216
217	if (javaString != NULL) {
218		const char *str;
219		str = (*env)->GetStringUTFChars(env, javaString, NULL);
220		if (str == NULL) {
221			noException = B_FALSE;
222		} else {
223			*nativeString = strdup(str);
224			(*env)->ReleaseStringUTFChars(env, javaString, str);
225			if (*nativeString == NULL) {
226				throw_memory_exception(env);
227				noException = B_FALSE;
228			}
229		}
230	} else {
231		*nativeString = NULL;
232	}
233
234	return (noException);
235}
236
237
238/*
239 * Call a java object's string getter method.
240 */
241boolean_t
242dd_get_str_attr(
243	JNIEnv *env,
244	jclass class,
245	int id,
246	jobject obj,
247	char **value) {
248
249	jmethodID methodID;
250	jstring jstr;
251
252	*value = NULL;
253
254	methodID = get_methodID(env, class, id);
255	if (methodID == NULL) {
256		return (B_FALSE);
257	}
258
259	jstr = (*env)->CallObjectMethod(env, obj, methodID);
260	if ((*env)->ExceptionOccurred(env) != NULL) {
261		return (B_FALSE);
262	}
263
264	if (jstr != NULL) {
265		*value = dd_jstring_to_native(env, jstr);
266		(*env)->DeleteLocalRef(env, jstr);
267		if (*value == NULL) {
268			return (B_FALSE);
269		}
270	}
271
272	return (B_TRUE);
273}
274
275/*
276 * Reads the DHCP configuration file and creates a dsvc_datastore_t.
277 */
278boolean_t
279dd_get_conf_datastore_t(JNIEnv *env, dsvc_datastore_t *dsp) {
280
281	dhcp_confopt_t *confopts;
282	int result;
283	boolean_t noException = B_TRUE;
284
285	dsp->d_resource = NULL;
286	dsp->d_location = NULL;
287	dsp->d_config = NULL;
288
289	result = read_dsvc_conf(&confopts);
290	if (result != 0) {
291		throw_no_defaults_exception(env);
292		noException = B_FALSE;
293	} else {
294		result = confopt_to_datastore(confopts, dsp);
295		if (result != DSVC_SUCCESS) {
296			throw_libdhcpsvc_exception(env, result);
297			noException = B_FALSE;
298		}
299		free_dsvc_conf(confopts);
300	}
301	return (noException);
302}
303
304/*
305 * Makes a dsvc_datastore_t using the DHCP configuration file and overriding
306 * the settings with the arguments if they are non-NULL.
307 */
308boolean_t
309dd_make_datastore_t(JNIEnv *env,
310    dsvc_datastore_t *dsp,
311    jobject jdatastore) {
312
313	jclass ds_class;
314	jthrowable e;
315
316	char *resource = NULL;
317	char *location = NULL;
318	char *config = NULL;
319	int version = DSVC_CUR_CONVER;
320
321	dsp->d_resource = NULL;
322	dsp->d_location = NULL;
323	dsp->d_config = NULL;
324
325	/* Locate the class we need */
326	ds_class = find_class(env, DS_CLASS);
327	if (ds_class == NULL) {
328		/* exception thrown */
329		return (B_FALSE);
330	}
331
332	/* Obtain the DHCP config file data store settings */
333	if (!dd_get_conf_datastore_t(env, dsp)) {
334		e = (*env)->ExceptionOccurred(env);
335		(*env)->ExceptionClear(env);
336		if (!is_no_defaults_exception(env, e)) {
337			(*env)->Throw(env, e);
338			return (B_FALSE);
339		}
340	}
341
342	/* Get the resource */
343	if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRC,
344	    jdatastore, &resource)) {
345		/* exception thrown */
346		dd_free_datastore_t(dsp);
347		return (B_FALSE);
348	}
349
350	/* If resource was passed in, then override config setting */
351	if (resource != NULL) {
352		free(dsp->d_resource);
353		dsp->d_resource = resource;
354		dsp->d_conver = DSVC_CUR_CONVER;
355	}
356
357	/* Validate the resource */
358	if (!dd_is_valid_data_store(env, dsp->d_resource)) {
359		if ((*env)->ExceptionOccurred(env) == NULL) {
360			throw_invalid_resource_exception(env, dsp->d_resource);
361		}
362		dd_free_datastore_t(dsp);
363		return (B_FALSE);
364	}
365
366	/* Get the location */
367	if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETLOC,
368	    jdatastore, &location)) {
369		/* exception thrown */
370		dd_free_datastore_t(dsp);
371		return (B_FALSE);
372	}
373
374	/* If location was passed in, then override config setting */
375	if (location != NULL) {
376		free(dsp->d_location);
377		dsp->d_location = location;
378	}
379
380	/* Must be defined */
381	if (dsp->d_location == NULL) {
382		throw_invalid_path_exception(env, dsp->d_location);
383		dd_free_datastore_t(dsp);
384		return (B_FALSE);
385	}
386
387	/* Get the config string */
388	if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRCCFG,
389	    jdatastore,	&config)) {
390		/* exception thrown */
391		dd_free_datastore_t(dsp);
392		return (B_FALSE);
393	}
394
395	/* If config string was passed in, then override config setting */
396	if (config != NULL) {
397		free(dsp->d_config);
398		dsp->d_config = config;
399	}
400
401	/* Get the version */
402	if (jdatastore != NULL && !dd_get_int_attr(env, ds_class, DS_GETVER,
403	    jdatastore,	&version)) {
404		/* exception thrown */
405		dd_free_datastore_t(dsp);
406		return (B_FALSE);
407	}
408
409	/* If version was passed in, then override config setting */
410	if (version != DSVC_CUR_CONVER) {
411		dsp->d_conver = version;
412	}
413
414	return (B_TRUE);
415}
416
417/*
418 * Frees the strings in a dsvc_datastore_t structure.
419 */
420void
421dd_free_datastore_t(dsvc_datastore_t *dsp) {
422	free(dsp->d_resource);
423	free(dsp->d_location);
424	free(dsp->d_config);
425}
426
427/*
428 * Returns the list of possible data stores for DHCP data.
429 * List returned is terminated with a NULL.
430 */
431char **
432dd_data_stores(JNIEnv *env)
433{
434	char **dsl = NULL;
435	char **module;
436	int count;
437	int ndx;
438	int rcode;
439
440	rcode = enumerate_dd(&module, &count);
441	if (rcode != DSVC_SUCCESS) {
442		throw_libdhcpsvc_exception(env, rcode);
443		return (NULL);
444	}
445
446	if (count == 0) {
447		return (NULL);
448	}
449
450	dsl = calloc((count + 1), sizeof (char *));
451	if (dsl == NULL) {
452		free_enumerated_dd(module, count);
453		throw_memory_exception(env);
454		return (NULL);
455	}
456
457	for (ndx = 0; ndx < count; ndx++) {
458		dsl[ndx] = strdup(module[ndx]);
459		if (dsl[ndx] == NULL) {
460			break;
461		}
462	}
463
464	free_enumerated_dd(module, count);
465
466	if (ndx != count) {
467		dd_free_data_stores(dsl);
468		throw_memory_exception(env);
469		dsl = NULL;
470	}
471
472	return (dsl);
473}
474
475/*
476 * Free a data store list created by dd_data_stores().
477 */
478void
479dd_free_data_stores(char **dsl)
480{
481	int i = 0;
482
483	if (dsl != NULL) {
484		for (i = 0; dsl[i] != NULL; ++i) {
485			free(dsl[i]);
486		}
487		free(dsl);
488	}
489}
490
491/*
492 * Send a signal to a process whose command name is as specified
493 */
494int
495dd_signal(char *fname, int sig)
496{
497	pid_t pid;
498
499	pid = dd_getpid(fname);
500	if (pid == (pid_t)-1) {
501		return (-1);
502	}
503
504	if (kill(pid, sig) != 0) {
505		return (errno);
506	} else {
507		return (0);
508	}
509}
510
511/*
512 * Return a process's pid
513 */
514pid_t
515dd_getpid(char *fname)
516{
517	DIR *dirptr;
518	dirent_t *direntptr;
519	psinfo_t psinfo;
520	int proc_fd;
521	char buf[MAXPATHLEN];
522	pid_t retval = (pid_t)-1;
523
524	/*
525	 * Read entries in /proc, each one is in turn a directory
526	 * containing files relating to the process's state.  We read
527	 * the psinfo file to get the command name.
528	 */
529	dirptr = opendir(PROCFS_DIR);
530	if (dirptr == (DIR *) NULL) {
531		return (retval);
532	}
533	while ((direntptr = readdir(dirptr)) != NULL) {
534		(void) snprintf(buf, sizeof (buf), PROCFS_DIR"/%s/psinfo",
535		    direntptr->d_name);
536		if ((proc_fd = open(buf, O_RDONLY)) < 0) {
537			continue;	/* skip this one */
538		}
539		if (read(proc_fd, &psinfo, sizeof (psinfo)) > 0) {
540			if (strncmp(psinfo.pr_fname, fname, PRFNSZ) == 0) {
541				retval = psinfo.pr_pid;
542				(void) close(proc_fd);
543				break;
544			}
545		}
546		(void) close(proc_fd);
547	}
548	(void) closedir(dirptr);
549	return (retval);
550}
551
552
553/*
554 * Get list of physical, non-loopback interfaces for the system.  Those are
555 * the ones in.dhcpd will support.
556 */
557struct ip_interface **
558dd_get_interfaces()
559{
560	int s;
561	struct ifconf ifc;
562	int num_ifs;
563	int i;
564	struct ifreq *ifr;
565	struct ip_interface **ret = NULL;
566	struct ip_interface **tmpret;
567	int retcnt = 0;
568	struct sockaddr_in *sin;
569
570	/*
571	 * Open socket, needed for doing the ioctls.  Then get number of
572	 * interfaces so we know how much memory to allocate, then get
573	 * all the interface configurations.
574	 */
575	s = socket(AF_INET, SOCK_DGRAM, 0);
576	if (ioctl(s, SIOCGIFNUM, &num_ifs) < 0) {
577		(void) close(s);
578		return (NULL);
579	}
580	ifc.ifc_len = num_ifs * sizeof (struct ifreq);
581	ifc.ifc_buf = malloc(ifc.ifc_len);
582	if (ifc.ifc_buf == NULL) {
583		(void) close(s);
584		return (NULL);
585	}
586	if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
587		free(ifc.ifc_buf);
588		(void) close(s);
589		return (NULL);
590	}
591
592	/*
593	 * For each interface, stuff its name, address and netmask into the
594	 * structure that we return.  Filter out loopback and virtual
595	 * interfaces as they are of no interest for DHCP.
596	 */
597	for (i = 0, ifr = ifc.ifc_req; i < num_ifs; ++i, ++ifr) {
598		if (strchr(ifr->ifr_name, ':') != NULL) {
599			continue;	/* Ignore a virtual interface */
600		}
601		if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) {
602			continue;	/* Can't get flags? Ignore it. */
603		}
604
605		if ((ifr->ifr_flags & IFF_LOOPBACK) ||
606		    !(ifr->ifr_flags & IFF_UP)) {
607			continue;	/* Ignore if loopback or down */
608		}
609		/* Get more space to store this in */
610		tmpret = realloc(ret,
611		    (retcnt+1)*sizeof (struct ip_interface *));
612		if (tmpret == NULL) {
613			while (retcnt-- > 0)
614				free(ret[retcnt]);
615			free(ret);
616			free(ifc.ifc_buf);
617			(void) close(s);
618			return (NULL);
619		}
620		ret = tmpret;
621		ret[retcnt] = malloc(sizeof (struct ip_interface));
622		if (ret[retcnt] == NULL) {
623			while (retcnt-- > 0)
624				free(ret[retcnt]);
625			free(ret);
626			free(ifc.ifc_buf);
627			(void) close(s);
628			return (NULL);
629		}
630		(void) strcpy(ret[retcnt]->name, ifr->ifr_name);
631		if (ioctl(s, SIOCGIFADDR, ifr) < 0) {
632			(void) close(s);
633			while (retcnt-- > 0) {
634				free(ret[retcnt]);
635			}
636			free(ret);
637			free(ifc.ifc_buf);
638			return (NULL);
639		}
640		/*LINTED - alignment*/
641		sin = (struct sockaddr_in *)&ifr->ifr_addr;
642		ret[retcnt]->addr = sin->sin_addr;
643
644		if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) {
645			(void) close(s);
646			while (retcnt-- > 0) {
647				free(ret[retcnt]);
648			}
649			free(ret);
650			free(ifc.ifc_buf);
651			return (NULL);
652		}
653		/*LINTED - alignment*/
654		sin = (struct sockaddr_in *)&ifr->ifr_addr;
655		ret[retcnt]->mask = sin->sin_addr;
656		++retcnt;
657	}
658
659	/* Null-terminate the list */
660	if (retcnt > 0) {
661		tmpret = realloc(ret,
662		    (retcnt+1)*sizeof (struct ip_interface *));
663		if (tmpret == NULL) {
664			while (retcnt-- > 0)
665				free(ret[retcnt]);
666			free(ret);
667			free(ifc.ifc_buf);
668			(void) close(s);
669			return (NULL);
670		}
671		ret = tmpret;
672		ret[retcnt] = NULL;
673	}
674	(void) close(s);
675	free(ifc.ifc_buf);
676	return (ret);
677}
678