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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }