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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <wordexp.h>
27 #include <string.h>
28 #include <malloc.h>
29 #include <sys/signal.h>
30 #include <libintl.h>
31 #include <arpa/inet.h>
32 #include <errno.h>
33 #include <dhcp_svc_private.h>
34 #include <dhcp_svc_confkey.h>
35 #include <jni.h>
36 #include <libscf.h>
37 #include <com_sun_dhcpmgr_bridge_Bridge.h>
38
39 #include "exception.h"
40 #include "dd_misc.h"
41 #include "class_cache.h"
42
43 #define DHCP_SERVER_INST "svc:/network/dhcp-server:default"
44
45 #define DHCPD_FNAME "in.dhcpd"
46 #define CONFOPT_MODE 0644
47
48 /*
49 * Gets called when the library is loaded.
50 */
51 /*ARGSUSED*/
52 JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM * jvm,void * reserved)53 JNI_OnLoad(
54 JavaVM *jvm,
55 void *reserved)
56 {
57 JNIEnv *env;
58
59 if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)) {
60 return (JNI_ERR);
61 }
62
63 init_class_cache();
64 return (JNI_VERSION_1_2);
65 }
66
67 /*
68 * Determine whether an upgrade of the datastore is necessary.
69 */
70 /*ARGSUSED*/
71 JNIEXPORT jboolean JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_isVersionCurrent(JNIEnv * env,jobject obj)72 Java_com_sun_dhcpmgr_bridge_Bridge_isVersionCurrent(
73 JNIEnv *env,
74 jobject obj)
75 {
76 dsvc_datastore_t datastore;
77 int cfgVersion;
78 int curVersion;
79 int rcode;
80 jboolean result = JNI_FALSE;
81
82 /* Get the data store configuration */
83 if (dd_get_conf_datastore_t(env, &datastore)) {
84 cfgVersion = datastore.d_conver;
85
86 datastore.d_conver = DSVC_CUR_CONVER;
87 free(datastore.d_location);
88 datastore.d_location = NULL;
89 rcode = status_dd(&datastore);
90 if (rcode != DSVC_SUCCESS) {
91 throw_libdhcpsvc_exception(env, rcode);
92 } else {
93 curVersion = datastore.d_conver;
94
95 if (curVersion == cfgVersion) {
96 result = JNI_TRUE;
97 }
98 }
99 dd_free_datastore_t(&datastore);
100 }
101
102 return (result);
103 }
104
105 /*
106 * Retrieve the data store object for the specified resource.
107 */
108 /*ARGSUSED*/
109 JNIEXPORT jobject JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_getDataStore(JNIEnv * env,jobject obj,jstring jresource)110 Java_com_sun_dhcpmgr_bridge_Bridge_getDataStore(
111 JNIEnv *env,
112 jobject obj,
113 jstring jresource)
114 {
115 jclass ds_class;
116 jmethodID ds_cons;
117 jobject dsObject;
118 jboolean avail;
119 jint version;
120 dsvc_datastore_t datastore;
121 char *resource;
122
123 /* Make sure we have the classes & methods we need */
124 ds_class = find_class(env, DS_CLASS);
125 if (ds_class == NULL) {
126 /* exception thrown */
127 return (NULL);
128 }
129 ds_cons = get_methodID(env, ds_class, DS_CONS);
130 if (ds_cons == NULL) {
131 /* exception thrown */
132 return (NULL);
133 }
134
135 /* Retrieve the resource argument */
136 if (!dd_jstring_to_UTF(env, jresource, &resource)) {
137 /* exception thrown */
138 return (NULL);
139 }
140
141 datastore.d_conver = DSVC_CUR_CONVER;
142 datastore.d_resource = resource;
143 datastore.d_location = NULL;
144 avail = JNI_FALSE;
145 if (status_dd(&datastore) == DSVC_SUCCESS) {
146 avail = JNI_TRUE;
147 version = datastore.d_conver;
148 }
149
150 dsObject = (*env)->NewObject(env, ds_class, ds_cons,
151 jresource, version, avail);
152
153 free(resource);
154 return (dsObject);
155 }
156
157 /*
158 * Retrieve the list of data stores available for DHCP. Returns an array of
159 * DHCP datastore names.
160 */
161 /*ARGSUSED*/
162 JNIEXPORT jobjectArray JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_getDataStores(JNIEnv * env,jobject obj)163 Java_com_sun_dhcpmgr_bridge_Bridge_getDataStores(
164 JNIEnv *env,
165 jobject obj)
166 {
167 jclass ds_class;
168 jmethodID ds_cons;
169 jobjectArray jlist = NULL;
170 jobject jobj;
171 jstring jstr;
172 jboolean avail;
173 jint version;
174 char **list;
175 dsvc_datastore_t datastore;
176 int i, len;
177
178 /* Make sure we have the classes & methods we need */
179 ds_class = find_class(env, DS_CLASS);
180 if (ds_class == NULL) {
181 /* exception thrown */
182 return (NULL);
183 }
184 ds_cons = get_methodID(env, ds_class, DS_CONS);
185 if (ds_cons == NULL) {
186 /* exception thrown */
187 return (NULL);
188 }
189
190 /* Get the list */
191 list = dd_data_stores(env);
192 if ((*env)->ExceptionOccurred(env) != NULL) {
193 return (NULL);
194 }
195
196 /* Compute the length of the array, store in len */
197 ARRAY_LENGTH(list, len);
198
199 /* Construct the array */
200 jlist = (*env)->NewObjectArray(env, len, ds_class, NULL);
201 if (jlist == NULL) {
202 /* exception thrown */
203 dd_free_data_stores(list);
204 return (NULL);
205 }
206
207 /* For each store, create an object and add it to the array */
208 for (i = 0; i < len; ++i) {
209
210 jstr = (*env)->NewStringUTF(env, list[i]);
211 if (jstr == NULL) {
212 /* exception thrown */
213 break;
214 }
215
216 datastore.d_conver = DSVC_CUR_CONVER;
217 datastore.d_resource = list[i];
218 datastore.d_location = NULL;
219 avail = JNI_FALSE;
220 if (status_dd(&datastore) == DSVC_SUCCESS) {
221 avail = JNI_TRUE;
222 version = datastore.d_conver;
223 }
224
225 jobj = (*env)->NewObject(env, ds_class, ds_cons,
226 jstr, version, avail);
227 if (jobj == NULL) {
228 /* exception thrown */
229 break;
230 }
231
232 (*env)->SetObjectArrayElement(env, jlist, i, jobj);
233 if ((*env)->ExceptionOccurred(env) != NULL) {
234 break;
235 }
236 }
237
238 dd_free_data_stores(list);
239 return (jlist);
240 }
241
242 /*
243 * Read the config file for DHCP and return its contents as a DhcpdOptions
244 * object.
245 */
246 /*ARGSUSED*/
247 JNIEXPORT jobject JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_readDefaults(JNIEnv * env,jobject obj)248 Java_com_sun_dhcpmgr_bridge_Bridge_readDefaults(
249 JNIEnv *env,
250 jobject obj)
251 {
252 jclass cfg_class;
253 jmethodID cfg_cons;
254 jmethodID cfg_set;
255 jobject cfgobj = NULL;
256 dhcp_confopt_t *cfgs, *tcfgs;
257
258 /* Make sure we have the classes & methods we need */
259 cfg_class = find_class(env, CFG_CLASS);
260 if (cfg_class == NULL) {
261 /* exception thrown */
262 return (NULL);
263 }
264 cfg_cons = get_methodID(env, cfg_class, CFG_CONS);
265 if (cfg_cons == NULL) {
266 /* exception thrown */
267 return (NULL);
268 }
269 cfg_set = get_methodID(env, cfg_class, CFG_SET);
270 if (cfg_set == NULL) {
271 /* exception thrown */
272 return (NULL);
273 }
274
275 /* Get the data */
276 if (read_dsvc_conf(&cfgs) != 0) {
277 throw_bridge_exception(env, strerror(errno));
278 } else {
279 /* Construct returned options object */
280 cfgobj = (*env)->NewObject(env, cfg_class, cfg_cons);
281 if (cfgobj == NULL) {
282 /* exception thrown */
283 free_dsvc_conf(cfgs);
284 return (NULL);
285 }
286
287 /* Load the option settings into the options object */
288 tcfgs = cfgs;
289 for (;;) {
290 if (cfgs->co_type == DHCP_COMMENT) {
291 (*env)->CallVoidMethod(env, cfgobj, cfg_set,
292 (*env)->NewStringUTF(env, cfgs->co_key),
293 (*env)->NewStringUTF(env, ""), JNI_TRUE);
294 } else {
295 if (cfgs->co_key == NULL) {
296 break;
297 }
298 (*env)->CallVoidMethod(env, cfgobj, cfg_set,
299 (*env)->NewStringUTF(env, cfgs->co_key),
300 (*env)->NewStringUTF(env, cfgs->co_value),
301 JNI_FALSE);
302 }
303 if ((*env)->ExceptionOccurred(env) != NULL) {
304 free_dsvc_conf(tcfgs);
305 return (NULL);
306 }
307 ++cfgs;
308 }
309 free_dsvc_conf(tcfgs);
310 }
311 return (cfgobj);
312 }
313
314 /*
315 * Write the DHCP config file. Takes a DhcpdOptions object as input
316 */
317 /*ARGSUSED*/
318 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_writeDefaults(JNIEnv * env,jobject obj,jobject jcfgs)319 Java_com_sun_dhcpmgr_bridge_Bridge_writeDefaults(
320 JNIEnv *env,
321 jobject obj,
322 jobject jcfgs)
323 {
324 jclass cfg_class;
325 jmethodID cfg_getall;
326 jclass res_class;
327 jmethodID res_getkey;
328 jmethodID res_getval;
329 jmethodID res_iscom;
330 jobjectArray resArray;
331 jsize reslen;
332 jobject jobj, resobj;
333 dhcp_confopt_t *cfgs;
334 int i;
335 jboolean comment;
336 const char *tmpstr;
337
338 /* Make sure we can get at the classes we need */
339 cfg_class = find_class(env, CFG_CLASS);
340 if (cfg_class == NULL) {
341 /* exception thrown */
342 return;
343 }
344 cfg_getall = get_methodID(env, cfg_class, CFG_GETALL);
345 if (cfg_getall == NULL) {
346 /* exception thrown */
347 return;
348 }
349 res_class = find_class(env, RES_CLASS);
350 if (res_class == NULL) {
351 /* exception thrown */
352 return;
353 }
354 res_getkey = get_methodID(env, res_class, RES_GETKEY);
355 res_getval = get_methodID(env, res_class, RES_GETVAL);
356 res_iscom = get_methodID(env, res_class, RES_ISCOM);
357 if (res_getkey == NULL || res_getval == NULL || res_iscom == NULL) {
358 /* exception thrown */
359 return;
360 }
361
362 /* Get the resource array from the config object */
363 resArray = (*env)->CallObjectMethod(env, jcfgs, cfg_getall);
364 if ((*env)->ExceptionOccurred(env) != NULL) {
365 return;
366 }
367 reslen = (*env)->GetArrayLength(env, resArray);
368 /* Allocate array to convert into; extra zero'd item to signal end */
369 cfgs = calloc(reslen+1, sizeof (dhcp_confopt_t));
370 if (cfgs == NULL) {
371 throw_memory_exception(env);
372 return;
373 }
374
375 /* Now copy data into local array */
376 for (i = 0; i < reslen; ++i) {
377 jobj = (*env)->GetObjectArrayElement(env, resArray, i);
378 if (jobj == NULL) {
379 /* exception thrown */
380 free_dsvc_conf(cfgs);
381 return;
382 }
383 /* Set record type */
384 comment = (*env)->CallBooleanMethod(env, jobj, res_iscom);
385 if ((*env)->ExceptionOccurred(env) != NULL) {
386 return;
387 }
388 if (comment == JNI_TRUE) {
389 cfgs[i].co_type = DHCP_COMMENT;
390 } else {
391 cfgs[i].co_type = DHCP_KEY;
392 }
393 /*
394 * Get the key from the object, convert to a char *,
395 * and then duplicate into the cfgs array so that
396 * free_dsvc_conf can be used correctly.
397 * Do the same thing for the value.
398 */
399 resobj = (*env)->CallObjectMethod(env, jobj, res_getkey);
400 tmpstr = (*env)->GetStringUTFChars(env, resobj, NULL);
401 if (tmpstr == NULL) {
402 /* exception thrown */
403 free_dsvc_conf(cfgs);
404 throw_bridge_exception(env,
405 gettext("Error converting key"));
406 return;
407 }
408 cfgs[i].co_key = strdup(tmpstr);
409 (*env)->ReleaseStringUTFChars(env, resobj, tmpstr);
410 if (cfgs[i].co_key == NULL) {
411 /* Out of memory, fail */
412 free_dsvc_conf(cfgs);
413 throw_memory_exception(env);
414 return;
415 }
416 resobj = (*env)->CallObjectMethod(env, jobj, res_getval);
417 tmpstr = (*env)->GetStringUTFChars(env, resobj, NULL);
418 if (tmpstr == NULL) {
419 free_dsvc_conf(cfgs);
420 throw_bridge_exception(env,
421 gettext("Error converting value"));
422 return;
423 }
424 cfgs[i].co_value = strdup(tmpstr);
425 (*env)->ReleaseStringUTFChars(env, resobj, tmpstr);
426 if (cfgs[i].co_value == NULL) {
427 /* Out of memory, fail */
428 free_dsvc_conf(cfgs);
429 throw_memory_exception(env);
430 return;
431 }
432 }
433
434 /* Now write the new data */
435 if (write_dsvc_conf(cfgs, CONFOPT_MODE) != 0) {
436 throw_bridge_exception(env, strerror(errno));
437 }
438 free_dsvc_conf(cfgs);
439 }
440
441 /*
442 * Remove the DHCP config file
443 */
444 /*ARGSUSED*/
445 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_removeDefaults(JNIEnv * env,jobject obj)446 Java_com_sun_dhcpmgr_bridge_Bridge_removeDefaults(
447 JNIEnv *env,
448 jobject obj)
449 {
450 if (delete_dsvc_conf() != 0) {
451 throw_bridge_exception(env, strerror(errno));
452 }
453 }
454
455 /*
456 * Start up the daemon.
457 */
458 /*ARGSUSED*/
459 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_startup(JNIEnv * env,jobject obj)460 Java_com_sun_dhcpmgr_bridge_Bridge_startup(
461 JNIEnv *env,
462 jobject obj)
463 {
464 char *s;
465 int ret;
466
467 /*
468 * We first get the current state of the server according to
469 * svc.startd; if it's "disabled", we can just enable it.
470 * In any other case, we want to send a refresh so that
471 * dependencies are re-evaluated, which will be the case if the
472 * service was marked enabled by the profile, yet the
473 * config file didn't exist to allow it to run.
474 */
475 if ((s = smf_get_state(DHCP_SERVER_INST)) != NULL) {
476 if (strcmp(SCF_STATE_STRING_DISABLED, s) == 0)
477 ret = smf_enable_instance(DHCP_SERVER_INST, 0);
478 else
479 ret = smf_refresh_instance(DHCP_SERVER_INST);
480 free(s);
481 if (ret == 0)
482 return;
483 }
484
485 /* Something wasn't right, return exception with error from smf */
486 throw_bridge_exception(env, scf_strerror(scf_error()));
487 }
488
489 /*
490 * Shut down the daemon.
491 */
492 /*ARGSUSED*/
493 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_shutdown(JNIEnv * env,jobject obj)494 Java_com_sun_dhcpmgr_bridge_Bridge_shutdown(
495 JNIEnv *env,
496 jobject obj)
497 {
498 if (smf_disable_instance(DHCP_SERVER_INST, 0) != 0) {
499 throw_bridge_exception(env, scf_strerror(scf_error()));
500 }
501 }
502
503 /*
504 * Tell the daemon to re-read the dhcptab.
505 */
506 /*ARGSUSED*/
507 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_reload(JNIEnv * env,jobject obj)508 Java_com_sun_dhcpmgr_bridge_Bridge_reload(
509 JNIEnv *env,
510 jobject obj)
511 {
512 int err;
513
514 if ((err = dd_signal(DHCPD_FNAME, SIGHUP)) != 0) {
515 if (err == -1) {
516 /* dd_signal couldn't find in.dhcpd running */
517 throw_not_running_exception(env);
518 } else {
519 throw_bridge_exception(env, strerror(err));
520 }
521 }
522 }
523
524 /*
525 * Make the resource location.
526 */
527 /*ARGSUSED*/
528 JNIEXPORT void JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_makeLocation(JNIEnv * env,jobject obj,jobject jdatastore)529 Java_com_sun_dhcpmgr_bridge_Bridge_makeLocation(
530 JNIEnv *env,
531 jobject obj,
532 jobject jdatastore)
533 {
534 dsvc_datastore_t datastore;
535 int rcode;
536
537 /* Create a dsvc_datastore_t using args and DHCP config file */
538 if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
539 /* exception thrown */
540 return;
541 }
542
543 /* If the location does not already exist, go create it. */
544 if (status_dd(&datastore) != DSVC_SUCCESS) {
545 rcode = mklocation_dd(&datastore);
546 if (rcode != DSVC_SUCCESS) {
547 throw_libdhcpsvc_exception(env, rcode);
548 }
549 }
550
551 dd_free_datastore_t(&datastore);
552 }
553
554 /*
555 * Check if the server is running; returns true if so, false if not.
556 */
557 /*ARGSUSED*/
558 JNIEXPORT jboolean JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_isServerRunning(JNIEnv * env,jobject obj)559 Java_com_sun_dhcpmgr_bridge_Bridge_isServerRunning(
560 JNIEnv *env,
561 jobject obj)
562 {
563 if (dd_getpid(DAEMON_FNAME) != (pid_t)-1) {
564 return (JNI_TRUE);
565 } else {
566 return (JNI_FALSE);
567 }
568 }
569
570 /*
571 * Retrieve the list of interfaces on the system which are candidates for
572 * use by the DHCP daemon. Returns an array of IPInterface objects.
573 */
574 /*ARGSUSED*/
575 JNIEXPORT jobjectArray JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_getInterfaces(JNIEnv * env,jobject obj)576 Java_com_sun_dhcpmgr_bridge_Bridge_getInterfaces(
577 JNIEnv *env,
578 jobject obj)
579 {
580 jclass ipif_class;
581 jmethodID ipif_cons;
582 jobjectArray jlist = NULL;
583 jobject jobj;
584 jsize len;
585 struct ip_interface **list;
586 int i;
587
588 /* Locate the class and constructor we need */
589 ipif_class = find_class(env, IPIF_CLASS);
590 if (ipif_class == NULL) {
591 /* exception thrown */
592 return (NULL);
593 }
594 ipif_cons = get_methodID(env, ipif_class, IPIF_CONS);
595 if (ipif_cons == NULL) {
596 return (NULL);
597 }
598
599 /* Retrieve interface list */
600 list = dd_get_interfaces();
601 if (list == NULL) {
602 throw_bridge_exception(env,
603 gettext("Error in dd_get_interfaces"));
604 return (NULL);
605 }
606 /* Compute length of list */
607 ARRAY_LENGTH(list, len);
608
609 /* Construct the array */
610 jlist = (*env)->NewObjectArray(env, len, ipif_class, NULL);
611 if (jlist == NULL) {
612 /* exception thrown */
613 for (i = 0; i < len; i++) {
614 free(list[i]);
615 }
616 free(list);
617 return (NULL);
618 }
619
620 /* For each interface, construct an object and add to the array */
621 for (i = 0; i < len; ++i) {
622 jobj = (*env)->NewObject(env, ipif_class, ipif_cons,
623 (*env)->NewStringUTF(env, list[i]->name),
624 (*env)->NewStringUTF(env, inet_ntoa(list[i]->addr)),
625 (*env)->NewStringUTF(env, inet_ntoa(list[i]->mask)));
626
627 if (jobj == NULL) {
628 /* exception thrown */
629 break;
630 }
631
632 (*env)->SetObjectArrayElement(env, jlist, i, jobj);
633 if ((*env)->ExceptionOccurred(env) != NULL) {
634 break;
635 }
636 }
637
638 for (i = 0; i < len; i++) {
639 free(list[i]);
640 }
641 free(list);
642
643 return (jlist);
644 }
645
646 /*
647 * Parse a line into arguments.
648 */
649 /*ARGSUSED*/
650 JNIEXPORT jobjectArray JNICALL
Java_com_sun_dhcpmgr_bridge_Bridge_getArguments(JNIEnv * env,jobject obj,jstring jline)651 Java_com_sun_dhcpmgr_bridge_Bridge_getArguments(
652 JNIEnv *env,
653 jobject obj,
654 jstring jline)
655 {
656 wordexp_t exp;
657 int flags = WRDE_NOCMD;
658 char *line;
659 jclass str_class;
660 jobjectArray jlist = NULL;
661 jstring jarg;
662 int i, ret;
663
664 /* Go ahead and get the class for a String class */
665 str_class = (*env)->GetObjectClass(env, jline);
666 if (str_class == NULL) {
667 /* exception thrown */
668 return (NULL);
669 }
670
671 /* Retrieve the line argument */
672 if (jline != NULL &&
673 (line = dd_jstring_to_native(env, jline)) == NULL) {
674 /* exception thrown */
675 return (NULL);
676 }
677
678 /* Retrieve argument list */
679 ret = wordexp(line, &exp, flags);
680 free(line);
681 if (ret != 0) {
682 throw_wordexp_exception(env, ret);
683 /* Free memory for the one error case where it's allocated */
684 if (ret == WRDE_NOSPACE)
685 wordfree(&exp);
686 return (NULL);
687 }
688
689 /* Construct the array */
690 jlist = (*env)->NewObjectArray(env, exp.we_wordc, str_class, NULL);
691 if (jlist == NULL) {
692 /* exception thrown */
693 wordfree(&exp);
694 return (NULL);
695 }
696
697 /* For each argument, create an object and add it to the array */
698 for (i = 0; i < exp.we_wordc; i++) {
699 jarg = dd_native_to_jstring(env, exp.we_wordv[i]);
700 if (jarg == NULL) {
701 /* exception thrown */
702 break;
703 }
704
705 (*env)->SetObjectArrayElement(env, jlist, i, jarg);
706 if ((*env)->ExceptionOccurred(env) != NULL) {
707 break;
708 }
709 }
710
711 wordfree(&exp);
712 return (jlist);
713 }
714