reindent-20030715
[openafs.git] / src / JAVA / libjafs / Server.c
1 /*
2  * Copyright (c) 2001-2002 International Business Machines Corp.
3  * All rights reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  * 
9  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
12  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
13  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
14  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
15  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
16  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
17  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
18  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
19  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20  */
21
22 #include "Internal.h"
23 #include "org_openafs_jafs_Server.h"
24
25 #include <afs_clientAdmin.h>
26 #include <afs_vosAdmin.h>
27 #include <afs_bosAdmin.h>
28 #include <afs_AdminCommonErrors.h>
29 #include <rx/rxkad.h>
30 #include <bnode.h>
31
32 //// definitions in Internal.c  //////////////////
33
34 extern jclass serverCls;
35 extern jfieldID server_nameField;
36 extern jfieldID server_nameField;
37 extern jfieldID server_databaseField;
38 extern jfieldID server_fileServerField;
39 extern jfieldID server_badDatabaseField;
40 extern jfieldID server_badFileServerField;
41 extern jfieldID server_IPAddressField;
42
43 extern jclass exectimeCls;
44 extern jfieldID exectime_HourField;
45 extern jfieldID exectime_MinField;
46 extern jfieldID exectime_SecField;
47 extern jfieldID exectime_DayField;
48 extern jfieldID exectime_NowField;
49 extern jfieldID exectime_NeverField;
50
51 extern jclass partitionCls;
52 extern jfieldID partition_cachedInfoField;
53
54 extern jclass keyCls;
55 extern jfieldID key_cachedInfoField;
56
57 extern jclass processCls;
58 extern jfieldID process_cachedInfoField;
59 extern jfieldID process_nameField;
60 //extern jfieldID process_serverHandleField;
61
62 extern jclass userCls;
63 extern jfieldID user_nameField;
64 extern jfieldID user_cachedInfoField;
65 //////////////////////////////////////////////////////////
66
67 ///// definition in jafs_Partition.c /////////////////
68
69 extern void fillPartitionInfo(JNIEnv * env, jobject partition,
70                               vos_partitionEntry_t partEntry);
71
72 ///////////////////////////////////////////////////
73
74 ///// definition in jafs_Key.c /////////////////
75
76 extern void fillKeyInfo(JNIEnv * env, jobject key, bos_KeyInfo_t keyEntry);
77
78 ///////////////////////////////////////////////////
79
80 ///// definition in jafs_Process.c /////////////////
81
82 extern void getProcessInfoChar(JNIEnv * env, void *serverHandle,
83                                const char *processName, jobject process);
84
85 ///////////////////////////////////////////////////
86
87 /**
88  * Extract the information from the given server entry and populate the
89  * given object
90  *
91  * env      the Java environment
92  * cellHandle    the handle of the cell to which the server belongs
93  * server      the Server object to populate with the info
94  * servEntry     the container of the server's information
95  */
96 void
97 fillServerInfo(JNIEnv * env, jint cellHandle, jobject server,
98                afs_serverEntry_t servEntry)
99 {
100     jstring jip;
101     jobjectArray jaddresses;
102     jstring jserver;
103     int i = 0;
104
105     // get the class fields if need be
106     if (serverCls == 0) {
107         internal_getServerClass(env, server);
108     }
109     // in case it's blank
110     jserver = (*env)->NewStringUTF(env, servEntry.serverName);
111     (*env)->SetObjectField(env, server, server_nameField, jserver);
112
113     // let's convert just the addresses in the address array into an IP
114     jaddresses =
115         (jobjectArray) (*env)->GetObjectField(env, server,
116                                               server_IPAddressField);
117     for (i = 0; i < 16; i++) {
118         if (servEntry.serverAddress[i] != 0) {
119             jip = (*env)->NewStringUTF(env, (char *)
120                                        inet_ntoa(htonl
121                                                  (servEntry.
122                                                   serverAddress[i])));
123             (*env)->SetObjectArrayElement(env, jaddresses, i, jip);
124         } else {
125             break;
126         }
127     }
128
129     // let's check if this is really a database server
130     (*env)->SetBooleanField(env, server, server_databaseField,
131                             servEntry.serverType & DATABASE_SERVER);
132     if (servEntry.serverType & DATABASE_SERVER) {
133         // for now, if it thinks it's a database server than it is
134         // later, add checks for database configuration, and actual 
135         // on-ness of the machine
136         (*env)->SetBooleanField(env, server, server_badDatabaseField, FALSE);
137     } else {
138         (*env)->SetBooleanField(env, server, server_badDatabaseField, FALSE);
139     }
140
141     // we should check to see if this is truly a file server or not
142     // it could just be an old remnant, left over inside the vldb that 
143     // should be removed.
144     // if it is a file server, mark it as such.  If not, mark it as faulty.
145     (*env)->SetBooleanField(env, server, server_fileServerField,
146                             servEntry.serverType & FILE_SERVER);
147     if (servEntry.serverType & FILE_SERVER) {
148
149         // to see if it's really a file server, make sure the 
150         // "fs" process is running
151         void *bosHandle;
152         afs_status_t ast;
153         bos_ProcessType_t processTypeT;
154         bos_ProcessInfo_t processInfoT;
155         char *fileServerProcessName = "fs";
156
157         // set the file server to true (it thinks it's a file server)
158         (*env)->SetBooleanField(env, server, server_fileServerField, TRUE);
159
160         if (!bos_ServerOpen
161             ((void *)cellHandle, servEntry.serverName, &bosHandle, &ast)) {
162             throwAFSException(env, ast);
163             return;
164         }
165         if (!bos_ProcessInfoGet
166             (bosHandle, fileServerProcessName, &processTypeT, &processInfoT,
167              &ast)) {
168             // if the machine does not have a fs process or is not responding 
169             // or is part of another cell
170             if (ast == BZNOENT || ast == -1 || ast == RXKADBADTICKET) {
171                 (*env)->SetBooleanField(env, server,
172                                         server_badFileServerField, TRUE);
173                 // otherwise
174             } else {
175                 throwAFSException(env, ast);
176                 return;
177             }
178         } else {
179             // it's good
180             (*env)->SetBooleanField(env, server, server_badFileServerField,
181                                     FALSE);
182         }
183     } else {
184         (*env)->SetBooleanField(env, server, server_badFileServerField,
185                                 FALSE);
186     }
187
188 }
189
190 /**
191  * Fills in the information fields of the provided Server. 
192  *
193  * env      the Java environment
194  * cls      the current Java class
195  * cellHandle    the handle of the cell to which the server belongs
196  * jname     the name of the server for which to get the information
197  * server     the Server object in which to fill in 
198  *                   the information
199  */
200 JNIEXPORT void JNICALL
201 Java_org_openafs_jafs_Server_getServerInfo(JNIEnv * env, jclass cls,
202                                            jint cellHandle, jstring jname,
203                                            jobject server)
204 {
205     char *name;
206     afs_status_t ast;
207     afs_serverEntry_t servEntry;
208
209     if (jname != NULL) {
210         name = getNativeString(env, jname);
211         if (name == NULL) {
212             throwAFSException(env, JAFSADMNOMEM);
213             return;
214         }
215     } else {
216         throwAFSException(env, JAFSNULLSERVER);
217         return;
218     }
219
220     // get the server entry
221     if (!afsclient_AFSServerGet((void *)cellHandle, name, &servEntry, &ast)) {
222         throwAFSException(env, ast);
223     }
224
225     fillServerInfo(env, cellHandle, server, servEntry);
226
227     free(name);
228 }
229
230 /**
231  * Returns the total number of partitions hosted by the server denoted by
232  * serverHandle, if the server is a fileserver.
233  *
234  * env      the Java environment
235  * cls      the current Java class
236  * cellHandle    the handle of the cell to which the server belongs
237  * serverHandle  the vos handle of the server to which the 
238  *                      partitions belong
239  * returns total number of partitions
240  */
241 JNIEXPORT jint JNICALL
242 Java_org_openafs_jafs_Server_getPartitionCount(JNIEnv * env, jclass cls,
243                                                jint cellHandle,
244                                                jint serverHandle)
245 {
246     afs_status_t ast;
247     void *iterationId;
248     vos_partitionEntry_t partEntry;
249     int i = 0;
250
251     if (!vos_PartitionGetBegin
252         ((void *)cellHandle, (void *)serverHandle, NULL, &iterationId,
253          &ast)) {
254         throwAFSException(env, ast);
255         return -1;
256     }
257
258     while (vos_PartitionGetNext((void *)iterationId, &partEntry, &ast))
259         i++;
260
261     if (ast != ADMITERATORDONE) {
262         throwAFSException(env, ast);
263         return -1;
264     }
265
266     return i;
267 }
268
269 /**
270  * Begin the process of getting the partitions on a server.  Returns 
271  * an iteration ID to be used by subsequent calls to 
272  * getPartitionsNext and getPartitionsDone.  
273  *
274  * env      the Java environment
275  * cls      the current Java class
276  * cellHandle    the handle of the cell to which the server belongs
277  * serverHandle  the vos handle of the server to which the 
278  *                      partitions belong
279  * returns an iteration ID
280  */
281 JNIEXPORT jint JNICALL
282 Java_org_openafs_jafs_Server_getPartitionsBegin(JNIEnv * env, jclass cls,
283                                                 jint cellHandle,
284                                                 jint serverHandle)
285 {
286     afs_status_t ast;
287     void *iterationId;
288
289     if (!vos_PartitionGetBegin
290         ((void *)cellHandle, (void *)serverHandle, NULL, &iterationId,
291          &ast)) {
292         throwAFSException(env, ast);
293         return;
294     }
295
296     return (jint) iterationId;
297
298 }
299
300 /**
301  * Returns the next partition of the server.  Returns null 
302  * if there are no more partitions.
303  *
304  * env      the Java environment
305  * cls      the current Java class
306  * iterationId   the iteration ID of this iteration
307  * returns the name of the next partition of the server
308  */
309 JNIEXPORT jstring JNICALL
310 Java_org_openafs_jafs_Server_getPartitionsNextString(JNIEnv * env, jclass cls,
311                                                      jint iterationId)
312 {
313     afs_status_t ast;
314     jstring jpartition;
315     vos_partitionEntry_t partEntry;
316
317     if (!vos_PartitionGetNext((void *)iterationId, &partEntry, &ast)) {
318         if (ast == ADMITERATORDONE) {
319             return NULL;
320         } else {
321             throwAFSException(env, ast);
322             return;
323         }
324     }
325
326     jpartition = (*env)->NewStringUTF(env, partEntry.name);
327     return jpartition;
328
329 }
330
331 /**
332  * Fills the next partition object of the server.  Returns 0 if there
333  * are no more partitions, != 0 otherwise
334  *
335  * env      the Java environment
336  * cls      the current Java class
337  * iterationId   the iteration ID of this iteration
338  * thePartition   the Partition object in which to fill the 
339  *                       values of the next partition
340  * returns 0 if there are no more servers, != 0 otherwise
341  */
342 JNIEXPORT jint JNICALL
343 Java_org_openafs_jafs_Server_getPartitionsNext(JNIEnv * env, jclass cls,
344                                                jint iterationId,
345                                                jobject jpartitionObject)
346 {
347     afs_status_t ast;
348     vos_partitionEntry_t partEntry;
349
350     if (!vos_PartitionGetNext((void *)iterationId, &partEntry, &ast)) {
351         if (ast == ADMITERATORDONE) {
352             return 0;
353         } else {
354             throwAFSException(env, ast);
355             return 0;
356         }
357     }
358
359     fillPartitionInfo(env, jpartitionObject, partEntry);
360
361     // get the class fields if need be
362     if (partitionCls == 0) {
363         internal_getPartitionClass(env, jpartitionObject);
364     }
365     (*env)->SetBooleanField(env, jpartitionObject, partition_cachedInfoField,
366                             TRUE);
367
368
369     return 1;
370
371 }
372
373 /**
374  * Signals that the iteration is complete and will not be accessed anymore.
375  *
376  * env      the Java environment
377  * cls      the current Java class
378  * iterationId   the iteration ID of this iteration
379  */
380 JNIEXPORT void JNICALL
381 Java_org_openafs_jafs_Server_getPartitionsDone(JNIEnv * env, jclass cls,
382                                                jint iterationId)
383 {
384     afs_status_t ast;
385
386     if (!vos_PartitionGetDone((void *)iterationId, &ast)) {
387         throwAFSException(env, ast);
388         return;
389     }
390
391 }
392
393 /**
394  * Adds the given to name to the list of bos administrators on that server.
395  *
396  * env      the Java environment
397  * cls      the current Java class
398  * serverHandle  the bos handle of the server to which the 
399  *                      partitions belong
400  * jnewAdmin   the name of the admin to add to the list
401  */
402 JNIEXPORT void JNICALL
403 Java_org_openafs_jafs_Server_addBosAdmin(JNIEnv * env, jclass cls,
404                                          jint serverHandle, jstring jnewAdmin)
405 {
406     afs_status_t ast;
407     char *newAdmin;
408
409     if (jnewAdmin != NULL) {
410         newAdmin = getNativeString(env, jnewAdmin);
411         if (newAdmin == NULL) {
412             throwAFSException(env, JAFSADMNOMEM);
413             return;
414         }
415     } else {
416         throwAFSException(env, JAFSNULLUSER);
417         return;
418     }
419
420     if (!bos_AdminCreate((void *)serverHandle, newAdmin, &ast)) {
421         throwAFSException(env, ast);
422     }
423
424     free(newAdmin);
425
426 }
427
428 /**
429  * Removes the given to name from the list of bos administrators on 
430  * that server.
431  *
432  * env      the Java environment
433  * cls      the current Java class
434  * serverHandle  the bos handle of the server to which the 
435  *                      partitions belong
436  * joldAdmin   the name of the admin to remove from the list
437  */
438 JNIEXPORT void JNICALL
439 Java_org_openafs_jafs_Server_removeBosAdmin(JNIEnv * env, jclass cls,
440                                             jint serverHandle,
441                                             jstring joldAdmin)
442 {
443     afs_status_t ast;
444     char *oldAdmin;
445
446     if (joldAdmin != NULL) {
447         oldAdmin = getNativeString(env, joldAdmin);
448         if (oldAdmin == NULL) {
449             throwAFSException(env, JAFSADMNOMEM);
450             return;
451         }
452     } else {
453         throwAFSException(env, JAFSNULLUSER);
454         return;
455     }
456
457     if (!bos_AdminDelete((void *)serverHandle, oldAdmin, &ast)) {
458         throwAFSException(env, ast);
459     }
460
461     free(oldAdmin);
462 }
463
464 /**
465  * Returns the total number of BOS administrators associated with the server 
466  * denoted by serverHandle.
467  *
468  * env      the Java environment
469  * cls      the current Java class
470  * serverHandle  the vos handle of the server to which the 
471  *                      BOS admins belong
472  * returns total number of BOS administrators
473  */
474 JNIEXPORT jint JNICALL
475 Java_org_openafs_jafs_Server_getBosAdminCount(JNIEnv * env, jclass cls,
476                                               jint serverHandle)
477 {
478     afs_status_t ast;
479     void *iterationId;
480     char *admin;
481     jstring jadmin;
482     int i = 0;
483
484     if (!bos_AdminGetBegin((void *)serverHandle, &iterationId, &ast)) {
485         throwAFSException(env, ast);
486         return -1;
487     }
488
489     admin = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
490
491     if (!admin) {
492         throwAFSException(env, JAFSADMNOMEM);
493         return -1;
494     }
495
496     while (bos_AdminGetNext((void *)iterationId, admin, &ast))
497         i++;
498
499     free(admin);
500
501     if (ast != ADMITERATORDONE) {
502         throwAFSException(env, ast);
503         return -1;
504     }
505
506     return i;
507 }
508
509 /**
510  * Begin the process of getting the bos amdinistrators on a server.  Returns 
511  * an iteration ID to be used by subsequent calls to 
512  * getBosAdminsNext and getBosAdminsDone.  
513  *
514  * env      the Java environment
515  * cls      the current Java class
516  * serverHandle  the bos handle of the server to which the 
517  *                      partitions belong
518  * returns an iteration ID
519  */
520 JNIEXPORT jint JNICALL
521 Java_org_openafs_jafs_Server_getBosAdminsBegin(JNIEnv * env, jclass cls,
522                                                jint serverHandle)
523 {
524     afs_status_t ast;
525     void *iterationId;
526
527     if (!bos_AdminGetBegin((void *)serverHandle, &iterationId, &ast)) {
528         throwAFSException(env, ast);
529         return;
530     }
531
532     return (jint) iterationId;
533 }
534
535 /**
536  * Returns the next bos admin of the server.  Returns null 
537  * if there are no more admins.
538  *
539  * env      the Java environment
540  * cls      the current Java class
541  * iterationId   the iteration ID of this iteration
542  * returns the name of the next admin of the server
543  */
544 JNIEXPORT jstring JNICALL
545 Java_org_openafs_jafs_Server_getBosAdminsNextString(JNIEnv * env, jclass cls,
546                                                     jint iterationId)
547 {
548
549     afs_status_t ast;
550     jstring jadmin;
551     char *admin = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
552
553     if (!admin) {
554         throwAFSException(env, JAFSADMNOMEM);
555         return;
556     }
557
558     if (!bos_AdminGetNext((void *)iterationId, admin, &ast)) {
559         free(admin);
560         if (ast == ADMITERATORDONE) {
561             return NULL;
562         } else {
563             throwAFSException(env, ast);
564             return;
565         }
566     }
567
568     jadmin = (*env)->NewStringUTF(env, admin);
569     free(admin);
570     return jadmin;
571
572 }
573
574 /**
575  * Returns the next bos admin of the server.  Returns 0 if there
576  * are no more admins, != 0 otherwise.
577  *
578  * env      the Java environment
579  * cls      the current Java class
580  * cellHandle    the handle of the cell to which these admins belong
581  * iterationId   the iteration ID of this iteration
582  * juserObject   the user object in which to fill the values of this admin
583  * returns 0 if no more admins, != 0 otherwise
584  */
585 JNIEXPORT jint JNICALL
586 Java_org_openafs_jafs_Server_getBosAdminsNext(JNIEnv * env, jclass cls,
587                                               jint cellHandle,
588                                               jint iterationId,
589                                               jobject juserObject)
590 {
591     afs_status_t ast;
592     char *admin;
593     jstring jadmin;
594
595     admin = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
596
597     if (!admin) {
598         throwAFSException(env, JAFSADMNOMEM);
599         return;
600     }
601
602     if (!bos_AdminGetNext((void *)iterationId, admin, &ast)) {
603         free(admin);
604         if (ast == ADMITERATORDONE) {
605             return 0;
606         } else {
607             throwAFSException(env, ast);
608             return 0;
609         }
610     }
611
612     jadmin = (*env)->NewStringUTF(env, admin);
613
614     if (userCls == 0) {
615         internal_getUserClass(env, juserObject);
616     }
617
618     (*env)->SetObjectField(env, juserObject, user_nameField, jadmin);
619
620     getUserInfoChar(env, cellHandle, admin, juserObject);
621     (*env)->SetBooleanField(env, juserObject, user_cachedInfoField, TRUE);
622
623     free(admin);
624     return 1;
625
626 }
627
628 /**
629  * Signals that the iteration is complete and will not be accessed anymore.
630  *
631  * env      the Java environment
632  * cls      the current Java class
633  * iterationId   the iteration ID of this iteration
634  */
635 JNIEXPORT void JNICALL
636 Java_org_openafs_jafs_Server_getBosAdminsDone(JNIEnv * env, jclass cls,
637                                               jint iterationId)
638 {
639     afs_status_t ast;
640
641     if (!bos_AdminGetDone((void *)iterationId, &ast)) {
642         throwAFSException(env, ast);
643         return;
644     }
645 }
646
647 /**
648  * Returns the total number of keys hosted by the server denoted by
649  * serverHandle.
650  *
651  * env      the Java environment
652  * cls      the current Java class
653  * serverHandle  the vos handle of the server to which the 
654  *                      keys belong
655  * returns total number of keys
656  */
657 JNIEXPORT jint JNICALL
658 Java_org_openafs_jafs_Server_getKeyCount(JNIEnv * env, jclass cls,
659                                          jint serverHandle)
660 {
661     afs_status_t ast;
662     void *iterationId;
663     bos_KeyInfo_t keyEntry;
664     int i = 0;
665
666     if (!bos_KeyGetBegin((void *)serverHandle, &iterationId, &ast)) {
667         throwAFSException(env, ast);
668         return -1;
669     }
670
671     while (bos_KeyGetNext((void *)iterationId, &keyEntry, &ast))
672         i++;
673
674     if (ast != ADMITERATORDONE) {
675         throwAFSException(env, ast);
676         return -1;
677     }
678
679     return i;
680 }
681
682 /**
683  * Begin the process of getting the keys of a server.  Returns 
684  * an iteration ID to be used by subsequent calls to 
685  * getKeysNext and getKeysDone.  
686  *
687  * env      the Java environment
688  * cls      the current Java class
689  * serverHandle  the bos handle of the server to which the keys belong
690  * returns an iteration ID
691  */
692 JNIEXPORT jint JNICALL
693 Java_org_openafs_jafs_Server_getKeysBegin(JNIEnv * env, jclass cls,
694                                           jint serverHandle)
695 {
696     afs_status_t ast;
697     void *iterationId;
698
699     if (!bos_KeyGetBegin((void *)serverHandle, &iterationId, &ast)) {
700         throwAFSException(env, ast);
701         return;
702     }
703
704     return (jint) iterationId;
705 }
706
707 /**
708  * Returns the next key of the server.  Returns 0 if there
709  * are no more keys, != 0 otherwise.
710  *
711  * env      the Java environment
712  * cls      the current Java class
713  * iterationId   the iteration ID of this iteration
714  * jkeyObject   a Key object, in which to fill in the
715  *                 properties of the next key.
716  * returns 0 if there are no more keys, != 0 otherwise
717  */
718 JNIEXPORT jint JNICALL
719 Java_org_openafs_jafs_Server_getKeysNext(JNIEnv * env, jclass cls,
720                                          jint iterationId, jobject jkeyObject)
721 {
722
723     afs_status_t ast;
724     bos_KeyInfo_t keyEntry;
725
726     if (!bos_KeyGetNext((void *)iterationId, &keyEntry, &ast)) {
727         if (ast == ADMITERATORDONE) {
728             return 0;
729         } else {
730             throwAFSException(env, ast);
731             return 0;
732         }
733     }
734
735     fillKeyInfo(env, jkeyObject, keyEntry);
736
737     // get the class fields if need be
738     if (keyCls == 0) {
739         internal_getKeyClass(env, jkeyObject);
740     }
741
742     (*env)->SetBooleanField(env, jkeyObject, key_cachedInfoField, TRUE);
743
744     return 1;
745 }
746
747 /**
748  * Signals that the iteration is complete and will not be accessed anymore.
749  *
750  * env      the Java environment
751  * cls      the current Java class
752  * iterationId   the iteration ID of this iteration
753  */
754 JNIEXPORT void JNICALL
755 Java_org_openafs_jafs_Server_getKeysDone(JNIEnv * env, jclass cls,
756                                          jint iterationId)
757 {
758     afs_status_t ast;
759
760     if (!bos_KeyGetDone((void *)iterationId, &ast)) {
761         throwAFSException(env, ast);
762         return;
763     }
764 }
765
766 /**
767  * Returns the total number of processes hosted by the server denoted by
768  * serverHandle.
769  *
770  * env      the Java environment
771  * cls      the current Java class
772  * serverHandle  the vos handle of the server to which the 
773  *                      processes belong
774  * returns total number of processes
775  */
776 JNIEXPORT jint JNICALL
777 Java_org_openafs_jafs_Server_getProcessCount(JNIEnv * env, jclass cls,
778                                              jint serverHandle)
779 {
780     afs_status_t ast;
781     void *iterationId;
782     char *process;
783     jstring jprocess;
784     int i = 0;
785
786     if (!bos_ProcessNameGetBegin((void *)serverHandle, &iterationId, &ast)) {
787         throwAFSException(env, ast);
788         return -1;
789     }
790
791     process = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
792
793     if (!process) {
794         throwAFSException(env, JAFSADMNOMEM);
795         return -1;
796     }
797
798     while (bos_ProcessNameGetNext((void *)iterationId, process, &ast))
799         i++;
800
801     free(process);
802
803     if (ast != ADMITERATORDONE) {
804         throwAFSException(env, ast);
805         return -1;
806     }
807
808     return i;
809 }
810
811 /**
812  * Begin the process of getting the processes on a server.  Returns 
813  * an iteration ID to be used by subsequent calls to 
814  * getProcessesNext and getProcessesDone.  
815  *
816  * env      the Java environment
817  * cls      the current Java class
818  * serverHandle  the bos handle of the server to which the 
819  *                      processes belong
820  * returns an iteration ID
821  */
822 JNIEXPORT jint JNICALL
823 Java_org_openafs_jafs_Server_getProcessesBegin(JNIEnv * env, jclass cls,
824                                                jint serverHandle)
825 {
826     afs_status_t ast;
827     void *iterationId;
828
829     if (!bos_ProcessNameGetBegin((void *)serverHandle, &iterationId, &ast)) {
830         throwAFSException(env, ast);
831         return;
832     }
833
834     return (jint) iterationId;
835 }
836
837 /**
838  * Returns the next process of the server.  Returns null 
839  * if there are no more processes.
840  *
841  * env      the Java environment
842  * cls      the current Java class
843  * iterationId   the iteration ID of this iteration
844  * returns the name of the next process of the cell
845  */
846 JNIEXPORT jstring JNICALL
847 Java_org_openafs_jafs_Server_getProcessesNextString(JNIEnv * env, jclass cls,
848                                                     jint iterationId)
849 {
850     afs_status_t ast;
851     jstring jprocess;
852     char *process = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
853
854     if (!process) {
855         throwAFSException(env, JAFSADMNOMEM);
856         return;
857     }
858
859     if (!bos_ProcessNameGetNext((void *)iterationId, process, &ast)) {
860         free(process);
861         if (ast == ADMITERATORDONE) {
862             return NULL;
863         } else {
864             throwAFSException(env, ast);
865             return;
866         }
867     }
868
869     jprocess = (*env)->NewStringUTF(env, process);
870     free(process);
871     return jprocess;
872 }
873
874 /**
875  * Fills the next process object of the server.  Returns 0 if there
876  * are no more processes, != 0 otherwise.
877  *
878  * env      the Java environment
879  * cls      the current Java class
880  * serverHandle    the handle of the BOS server that hosts the process
881  * iterationId   the iteration ID of this iteration
882  * jprocessObject    the Process object in which to fill the 
883  *                          values of the next process
884  * returns 0 if there are no more processes, != otherwise
885  */
886 JNIEXPORT jint JNICALL
887 Java_org_openafs_jafs_Server_getProcessesNext(JNIEnv * env, jclass cls,
888                                               jint serverHandle,
889                                               jint iterationId,
890                                               jobject jprocessObject)
891 {
892     afs_status_t ast;
893     char *process = (char *)malloc(sizeof(char) * BOS_MAX_NAME_LEN);
894     jstring jprocess;
895
896     if (!process) {
897         throwAFSException(env, JAFSADMNOMEM);
898         return;
899     }
900
901     if (!bos_ProcessNameGetNext((void *)iterationId, process, &ast)) {
902         free(process);
903         if (ast == ADMITERATORDONE) {
904             return 0;
905         } else {
906             throwAFSException(env, ast);
907             return 0;
908         }
909     }
910     // get the class fields if need be
911     if (processCls == 0) {
912         internal_getProcessClass(env, jprocessObject);
913     }
914
915     jprocess = (*env)->NewStringUTF(env, process);
916     (*env)->SetObjectField(env, jprocessObject, process_nameField, jprocess);
917
918     getProcessInfoChar(env, (void *)serverHandle, process, jprocessObject);
919
920     (*env)->SetBooleanField(env, jprocessObject, process_cachedInfoField,
921                             TRUE);
922
923     free(process);
924     return 1;
925 }
926
927 /**
928  * Signals that the iteration is complete and will not be accessed anymore.
929  *
930  * env      the Java environment
931  * cls      the current Java class
932  * iterationId   the iteration ID of this iteration
933  */
934 JNIEXPORT void JNICALL
935 Java_org_openafs_jafs_Server_getProcessesDone(JNIEnv * env, jclass cls,
936                                               jint iterationId)
937 {
938     afs_status_t ast;
939
940     if (!bos_ProcessNameGetDone((void *)iterationId, &ast)) {
941         throwAFSException(env, ast);
942         return;
943     }
944 }
945
946 /**
947  * Salvages (restores consistency to) a volume, partition, or server
948  *
949  * env      the Java environment
950  * cls      the current Java class
951  * cellHandle    the handle of the cell to which the volume belongs
952  * serverHandle  the bos handle of the server on which the 
953  *                      volume resides
954  * jpartName  the name of the partition to salvage, 
955  *                   can be null only if volName is 
956  *                   null
957  * jvolName  the name of the volume to salvage, 
958  *                  can be null
959  * numSalvagers   the number of salvager processes to run in parallel
960  * jtempDir   directory to place temporary files, can be 
961  *                  null
962  * jlogFile    where salvager log will be written, can be 
963  *                   null
964  * inspectAllVolumes   whether or not to inspect all volumes, 
965  *                            not just those marked as active at crash
966  * removeBadlyDamaged   whether or not to remove a volume if it's 
967  *                             badly damaged
968  * writeInodes   whether or not to record a list of inodes modified
969  * writeRootInodes   whether or not to record a list of AFS 
970  *                          inodes owned by root
971  * forceDirectory   whether or not to salvage an entire directory 
972  *                         structure
973  * forceBlockReads   whether or not to force the salvager to read 
974  *                          the partition
975  *                          one block at a time and skip badly damaged 
976  *                          blocks.  Use if partition has disk errors
977  */
978 JNIEXPORT void JNICALL
979 Java_org_openafs_jafs_Server_salvage(JNIEnv * env, jclass cls,
980                                      jint cellHandle, jint serverHandle,
981                                      jstring jpartName, jstring jvolName,
982                                      jint numSalvagers, jstring jtempDir,
983                                      jstring jlogFile,
984                                      jboolean inspectAllVolumes,
985                                      jboolean removeBadlyDamaged,
986                                      jboolean writeInodes,
987                                      jboolean writeRootInodes,
988                                      jboolean forceDirectory,
989                                      jboolean forceBlockReads)
990 {
991     afs_status_t ast;
992     char *partName;
993     char *volName;
994     char *tempDir;
995     char *logFile;
996     vos_force_t force;
997     bos_SalvageDamagedVolumes_t sdv;
998     bos_WriteInodes_t wi;
999     bos_WriteRootInodes_t wri;
1000     bos_ForceDirectory_t forceD;
1001     bos_ForceBlockRead_t forceBR;
1002
1003     // convert strings
1004     if (jpartName != NULL) {
1005         partName = getNativeString(env, jpartName);
1006         if (!partName) {
1007             throwAFSException(env, JAFSADMNOMEM);
1008             return;
1009         }
1010     } else {
1011         partName = NULL;
1012     }
1013
1014     if (jvolName != NULL) {
1015         volName = getNativeString(env, jvolName);
1016         if (!volName) {
1017             if (partName != NULL)
1018                 free(partName);
1019             throwAFSException(env, JAFSADMNOMEM);
1020             return;
1021         }
1022     } else {
1023         volName = NULL;
1024     }
1025
1026     if (jtempDir != NULL) {
1027         tempDir = getNativeString(env, jtempDir);
1028         if (!tempDir) {
1029             if (partName != NULL)
1030                 free(partName);
1031             if (volName != NULL)
1032                 free(volName);
1033             throwAFSException(env, JAFSADMNOMEM);
1034             return;
1035         }
1036     } else {
1037         tempDir = NULL;
1038     }
1039
1040     if (jlogFile != NULL) {
1041         logFile = getNativeString(env, jlogFile);
1042         if (!logFile) {
1043             if (partName != NULL)
1044                 free(partName);
1045             if (volName != NULL)
1046                 free(volName);
1047             if (tempDir != NULL)
1048                 free(tempDir);
1049             throwAFSException(env, JAFSADMNOMEM);
1050             return;
1051         }
1052     } else {
1053         logFile = NULL;
1054     }
1055
1056     // deal with booleans
1057     if (inspectAllVolumes) {
1058         force = VOS_FORCE;
1059     } else {
1060         force = VOS_NORMAL;
1061     }
1062     if (removeBadlyDamaged) {
1063         sdv = BOS_DONT_SALVAGE_DAMAGED_VOLUMES;
1064     } else {
1065         sdv = BOS_SALVAGE_DAMAGED_VOLUMES;
1066     }
1067     if (writeInodes) {
1068         wi = BOS_SALVAGE_WRITE_INODES;
1069     } else {
1070         wi = BOS_SALVAGE_DONT_WRITE_INODES;
1071     }
1072     if (writeRootInodes) {
1073         wri = BOS_SALVAGE_WRITE_ROOT_INODES;
1074     } else {
1075         wri = BOS_SALVAGE_DONT_WRITE_ROOT_INODES;
1076     }
1077     if (forceDirectory) {
1078         forceD = BOS_SALVAGE_FORCE_DIRECTORIES;
1079     } else {
1080         forceD = BOS_SALVAGE_DONT_FORCE_DIRECTORIES;
1081     }
1082     if (forceBlockReads) {
1083         forceBR = BOS_SALVAGE_FORCE_BLOCK_READS;
1084     } else {
1085         forceBR = BOS_SALVAGE_DONT_FORCE_BLOCK_READS;
1086     }
1087
1088     //salvage!
1089     if (!bos_Salvage
1090         ((void *)cellHandle, (void *)serverHandle, partName, volName,
1091          (int)numSalvagers, tempDir, logFile, force, sdv, wi, wri, forceD,
1092          forceBR, &ast)) {
1093         throwAFSException(env, ast);
1094     }
1095     // release strings
1096     if (partName != NULL)
1097         free(partName);
1098     if (volName != NULL)
1099         free(volName);
1100     if (tempDir != NULL)
1101         free(tempDir);
1102     if (logFile != NULL)
1103         free(logFile);
1104
1105 }
1106
1107 /**
1108  *  Fills in the restart time fields of the given Server
1109  *  object. 
1110  *
1111  * env      the Java environment
1112  * cls      the current Java class
1113  * serverHandle  the bos handle of the server to which the key belongs
1114  * jtype  whether to get the general or binary restart. 
1115  *               Acceptable values are:
1116  *               org_opemafs_jafs_Server_RESTART_BINARY
1117  *               org_opemafs_jafs_Server_RESTART_GENERAL    
1118  * execTime   the ExecutableTime object, in which 
1119  *                   to fill the restart time fields
1120  */
1121 JNIEXPORT void JNICALL
1122 Java_org_openafs_jafs_Server_getRestartTime(JNIEnv * env, jclass cls,
1123                                             jint serverHandle, jint jtype,
1124                                             jobject exectime)
1125 {
1126     afs_status_t ast;
1127     bos_Restart_t type;
1128     bos_RestartTime_t time;
1129     jfieldID hourField;
1130     jfieldID minField;
1131     jfieldID secField;
1132     jfieldID dayField;
1133     jfieldID neverField;
1134     jfieldID nowField;
1135
1136     // get the class fields if need be
1137     if (exectimeCls == 0) {
1138         internal_getExecTimeClass(env, exectime);
1139     }
1140
1141     if (jtype == org_openafs_jafs_Server_RESTART_BINARY) {
1142         type = BOS_RESTART_DAILY;
1143     } else {
1144         type = BOS_RESTART_WEEKLY;
1145     }
1146
1147     hourField = exectime_HourField;
1148     minField = exectime_MinField;
1149     secField = exectime_SecField;
1150     dayField = exectime_DayField;
1151     neverField = exectime_NeverField;
1152     nowField = exectime_NowField;
1153
1154     if (!bos_ExecutableRestartTimeGet
1155         ((void *)serverHandle, type, &time, &ast)) {
1156         throwAFSException(env, ast);
1157         return;
1158     }
1159     // set now
1160     (*env)->SetBooleanField(env, exectime, nowField,
1161                             (time.mask & BOS_RESTART_TIME_NOW));
1162
1163     // set never
1164     (*env)->SetBooleanField(env, exectime, neverField,
1165                             (time.mask & BOS_RESTART_TIME_NEVER));
1166
1167     // set hour
1168     (*env)->SetShortField(env, exectime, hourField, time.hour);
1169
1170     // set minute
1171     (*env)->SetShortField(env, exectime, minField, time.min);
1172
1173     // set second
1174     (*env)->SetShortField(env, exectime, secField, time.sec);
1175
1176     // set day
1177     if (time.mask & BOS_RESTART_TIME_DAY) {
1178         (*env)->SetShortField(env, exectime, dayField, time.day);
1179     } else {
1180         (*env)->SetShortField(env, exectime, dayField, (jshort) - 1);
1181     }
1182
1183 }
1184
1185 /**
1186  *  Sets the restart time of the bos server.
1187  *
1188  * env      the Java environment
1189  * cls      the current Java class
1190  * serverHandle  the bos handle of the server to which the key belongs
1191  * jtype  whether this is to be a general or binary restart. 
1192  *               Acceptable values are:
1193  *               org_opemafs_jafs_Server_RESTART_BINARY
1194  *               org_opemafs_jafs_Server_RESTART_GENERAL
1195  * executableTime   the ExecutableTime object containing the 
1196  *                         desired information
1197  */
1198 JNIEXPORT void JNICALL
1199 Java_org_openafs_jafs_Server_setRestartTime(JNIEnv * env, jclass cls,
1200                                             jint serverHandle, jint jtype,
1201                                             jobject exectime)
1202 {
1203     afs_status_t ast;
1204     bos_Restart_t type;
1205     bos_RestartTime_t time;
1206     jboolean doHour;
1207     jboolean doMinute;
1208     jboolean doSecond;
1209     jboolean doDay;
1210     jboolean doNever;
1211     jboolean doNow;
1212     jshort hour;
1213     jshort minute;
1214     jshort second;
1215     jshort day;
1216     jfieldID hourField;
1217     jfieldID minField;
1218     jfieldID secField;
1219     jfieldID dayField;
1220     jfieldID neverField;
1221     jfieldID nowField;
1222
1223     // get the class fields if need be
1224     if (exectimeCls == 0) {
1225         internal_getExecTimeClass(env, exectime);
1226     }
1227
1228     if (jtype == org_openafs_jafs_Server_RESTART_BINARY) {
1229         type = BOS_RESTART_DAILY;
1230     } else {
1231         type = BOS_RESTART_WEEKLY;
1232     }
1233
1234     hourField = exectime_HourField;
1235     minField = exectime_MinField;
1236     secField = exectime_SecField;
1237     dayField = exectime_DayField;
1238     neverField = exectime_NeverField;
1239     nowField = exectime_NowField;
1240
1241     hour = (*env)->GetShortField(env, exectime, hourField);
1242     if (hour != 0) {
1243         doHour = TRUE;
1244     } else {
1245         doHour = FALSE;
1246     }
1247     minute = (*env)->GetShortField(env, exectime, minField);
1248     if (minute != 0) {
1249         doMinute = TRUE;
1250     } else {
1251         doMinute = FALSE;
1252     }
1253     second = (*env)->GetShortField(env, exectime, secField);
1254     if (second != 0) {
1255         doSecond = TRUE;
1256     } else {
1257         doSecond = FALSE;
1258     }
1259     day = (*env)->GetShortField(env, exectime, dayField);
1260     if (day != -1) {
1261         doDay = TRUE;
1262     } else {
1263         doDay = FALSE;
1264     }
1265     doNever = (*env)->GetBooleanField(env, exectime, neverField);
1266     doNow = (*env)->GetBooleanField(env, exectime, nowField);
1267
1268     bzero(&time, sizeof(time));
1269
1270     if (jtype == org_openafs_jafs_Server_RESTART_BINARY) {
1271         type = BOS_RESTART_DAILY;
1272     } else {
1273         type = BOS_RESTART_WEEKLY;
1274     }
1275
1276     if (doHour) {
1277         time.mask |= BOS_RESTART_TIME_HOUR;
1278     }
1279     if (doMinute) {
1280         time.mask |= BOS_RESTART_TIME_MINUTE;
1281     }
1282     if (doSecond) {
1283         time.mask |= BOS_RESTART_TIME_SECOND;
1284     }
1285     if (doDay) {
1286         time.mask |= BOS_RESTART_TIME_DAY;
1287     }
1288     if (doNever) {
1289         time.mask |= BOS_RESTART_TIME_NEVER;
1290     }
1291     if (doNow) {
1292         time.mask |= BOS_RESTART_TIME_NOW;
1293     }
1294
1295     time.hour = hour;
1296     time.min = minute;
1297     time.sec = second;
1298     time.day = day;
1299
1300     if (!bos_ExecutableRestartTimeSet((void *)serverHandle, type, time, &ast)) {
1301         throwAFSException(env, ast);
1302     }
1303 }
1304
1305 /**
1306  *  Synchronizes a particular server with the volume location database.
1307  *
1308  * env      the Java environment
1309  * cls      the current Java class
1310  * cellHandle    the handle of the cell to which the server belongs
1311  * serverHandle  the vos handle of the server     
1312  * partition   the id of the partition to sync, can be -1 to ignore
1313  */
1314 JNIEXPORT void JNICALL
1315 Java_org_openafs_jafs_Server_syncServerWithVLDB(JNIEnv * env, jclass cls,
1316                                                 jint cellHandle,
1317                                                 jint serverHandle,
1318                                                 jint partition)
1319 {
1320     afs_status_t ast;
1321     int *part;
1322
1323     if (partition == -1) {
1324         part = NULL;
1325     } else {
1326         part = (int *)&partition;
1327     }
1328
1329     if (!vos_ServerSync
1330         ((void *)cellHandle, (void *)serverHandle, NULL, part, &ast)) {
1331         throwAFSException(env, ast);
1332     }
1333 }
1334
1335 /**
1336  *  Synchronizes the volume location database with a particular server.
1337  *
1338  * env      the Java environment
1339  * cls      the current Java class
1340  * cellHandle    the handle of the cell to which the server belongs
1341  * serverHandle  the vos handle of the server     
1342  * partition   the id of the partition to sync, can be -1 to ignore
1343  * forceDeletion   whether or not to force the deletion of bad volumes
1344  */
1345 JNIEXPORT void JNICALL
1346 Java_org_openafs_jafs_Server_syncVLDBWithServer(JNIEnv * env, jclass cls,
1347                                                 jint cellHandle,
1348                                                 jint serverHandle,
1349                                                 jint partition,
1350                                                 jboolean forceDeletion)
1351 {
1352     afs_status_t ast;
1353     int *part;
1354     vos_force_t force;
1355
1356     if (partition == -1) {
1357         part = NULL;
1358     } else {
1359         part = (int *)&partition;
1360     }
1361
1362     if (forceDeletion) {
1363         force = VOS_FORCE;
1364     } else {
1365         force = VOS_NORMAL;
1366     }
1367
1368     if (!vos_VLDBSync
1369         ((void *)cellHandle, (void *)serverHandle, NULL, part, force, &ast)) {
1370         throwAFSException(env, ast);
1371     }
1372 }
1373
1374 /**
1375  * Start all server processes.
1376  *
1377  * env      the Java environment
1378  * cls      the current Java class
1379  * serverHandle  the bos handle of the server to which the 
1380  *                      processes belong
1381  */
1382 JNIEXPORT void JNICALL
1383 Java_org_openafs_jafs_Server_startAllProcesses(JNIEnv * env, jclass cls,
1384                                                jint serverHandle)
1385 {
1386     afs_status_t ast;
1387
1388     if (!bos_ProcessAllStart((void *)serverHandle, &ast)) {
1389         throwAFSException(env, ast);
1390         return;
1391     }
1392 }
1393
1394 /**
1395  * Stop all server processes.
1396  *
1397  * env      the Java environment
1398  * cls      the current Java class
1399  * serverHandle  the bos handle of the server to which the 
1400  *                      processes belong
1401  */
1402 JNIEXPORT void JNICALL
1403 Java_org_openafs_jafs_Server_stopAllProcesses(JNIEnv * env, jclass cls,
1404                                               jint serverHandle)
1405 {
1406     afs_status_t ast;
1407
1408     if (!bos_ProcessAllStop((void *)serverHandle, &ast)) {
1409         throwAFSException(env, ast);
1410         return;
1411     }
1412 }
1413
1414 /**
1415  * Restart all server processes.
1416  *
1417  * env      the Java environment
1418  * cls      the current Java class
1419  * serverHandle  the bos handle of the server to which the 
1420  *                      processes belong
1421  * restartBosServer   whether or not to restart the bos server as well
1422  */
1423 JNIEXPORT void JNICALL
1424 Java_org_openafs_jafs_Server_restartAllProcesses(JNIEnv * env, jclass cls,
1425                                                  jint serverHandle,
1426                                                  jboolean restartBosServer)
1427 {
1428     afs_status_t ast;
1429     bos_RestartBosServer_t rbs;
1430
1431     if (restartBosServer) {
1432         rbs = BOS_RESTART_BOS_SERVER;
1433     } else {
1434         rbs = BOS_DONT_RESTART_BOS_SERVER;
1435     }
1436
1437     if (!bos_ProcessAllStopAndRestart((void *)serverHandle, rbs, &ast)) {
1438         throwAFSException(env, ast);
1439         return;
1440     }
1441 }
1442
1443 /**
1444  * Retrieves a specified bos log from a server.  Right now this 
1445  * method will simply return a huge String containing the log, but 
1446  * hopefully we can devise a better way to make this work more efficiently.
1447  *
1448  * env      the Java environment
1449  * cls      the current Java class
1450  * serverHandle  the bos handle of the server to which the key belongs
1451  * jlogFile   the full path and name of the desired bos log
1452  */
1453 JNIEXPORT jstring JNICALL
1454 Java_org_openafs_jafs_Server_getLog(JNIEnv * env, jclass cls,
1455                                     jint serverHandle, jstring jlogFile)
1456 {
1457     afs_status_t ast;
1458     char *logFile;
1459     char *logData;
1460     unsigned long currInLogSize = 1;
1461     unsigned long currOutLogSize = 0;
1462     jstring logOut;
1463
1464     logData = (char *)malloc(sizeof(char) * currInLogSize);
1465     if (logData == NULL) {
1466         throwAFSException(env, JAFSADMNOMEM);
1467         return NULL;
1468     }
1469
1470     if (jlogFile != NULL) {
1471         logFile = getNativeString(env, jlogFile);
1472         if (logFile == NULL) {
1473             free(logData);
1474             throwAFSException(env, JAFSADMNOMEM);
1475             return NULL;
1476         }
1477     } else {
1478         free(logData);
1479         throwAFSException(env, JAFSNULLARG);
1480         return NULL;
1481     }
1482
1483     // check how big the log is . . .
1484     if (!bos_LogGet
1485         ((void *)serverHandle, logFile, &currOutLogSize, logData, &ast)) {
1486         // anything but not enough room in buffer
1487         if (ast != ADMMOREDATA) {
1488             free(logData);
1489             free(logFile);
1490             throwAFSException(env, ast);
1491             return NULL;
1492         }
1493     }
1494     // free previous allocation
1495     free(logData);
1496
1497     // increase log size (plus one for terminator)
1498     currInLogSize = currOutLogSize + 1;
1499
1500     // allocate buffer
1501     logData = (char *)malloc(sizeof(char) * currInLogSize);
1502     if (logData == NULL) {
1503         free(logFile);
1504         throwAFSException(env, JAFSADMNOMEM);
1505         return NULL;
1506     }
1507     // get the log for real
1508     if (!bos_LogGet
1509         ((void *)serverHandle, logFile, &currOutLogSize, logData, &ast)) {
1510         free(logData);
1511         free(logFile);
1512         throwAFSException(env, ast);
1513         return NULL;
1514     }
1515     // NULL-terminate
1516     logData[currOutLogSize] == '\0';
1517
1518     logOut = (*env)->NewStringUTF(env, logData);
1519
1520     free(logData);
1521     free(logFile);
1522
1523     return logOut;
1524 }
1525
1526
1527 /**
1528  * Executes any command on the specified server.
1529  *
1530  * env      the Java environment
1531  * cls      the current Java class
1532  * serverHandle  the bos handle of the server to which the key belongs
1533  * jcommand     the text of the commmand to execute
1534  */
1535 JNIEXPORT void JNICALL
1536 Java_org_openafs_jafs_Server_executeCommand(JNIEnv * env, jclass cls,
1537                                             jint serverHandle,
1538                                             jstring jcommand)
1539 {
1540     afs_status_t ast;
1541     char *command;
1542
1543     if (jcommand != NULL) {
1544         command = getNativeString(env, jcommand);
1545         if (command == NULL) {
1546             throwAFSException(env, JAFSADMNOMEM);
1547             return;
1548         }
1549     } else {
1550         throwAFSException(env, JAFSNULLARG);
1551         return;
1552     }
1553
1554     if (!bos_CommandExecute((void *)serverHandle, command, &ast)) {
1555         throwAFSException(env, ast);
1556     }
1557
1558     free(command);
1559 }
1560
1561 // reclaim global memory being used by this portion
1562 JNIEXPORT void JNICALL
1563 Java_org_openafs_jafs_Server_reclaimServerMemory(JNIEnv * env, jclass cls)
1564 {
1565     if (serverCls) {
1566         (*env)->DeleteGlobalRef(env, serverCls);
1567         serverCls = 0;
1568     }
1569 }