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 }