Bump version to 6.4-15
[LibreOffice.git] / sal / android / lo-bootstrap.c
blob0af8cfbb4cd3ad0087605f05b29834ebe93c9efa
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <time.h>
18 #include <dlfcn.h>
19 #include <fcntl.h>
20 #include <pthread.h>
21 #include <sys/mman.h>
22 #include <unistd.h>
24 #include <jni.h>
25 #include <zlib.h>
27 #include <android/log.h>
29 #include "uthash.h"
31 #include <osl/detail/android-bootstrap.h>
33 #undef LOGI
35 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__))
36 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "lo-bootstrap", __VA_ARGS__))
38 #define MAX(a,b) ((a) > (b) ? (a) : (b))
40 // TODO: workaround for unified headers migration - only made available when
41 // __USE_BSD or __BIONIC__ are defined, so just add those here...
42 #define letoh16(x) (x)
43 #define letoh32(x) (x)
45 struct engine {
46 int dummy;
49 /* These are valid / used in all apps. */
50 const char *data_dir;
51 const char *cache_dir;
52 void *apk_file;
53 int apk_file_size;
54 JavaVM *the_java_vm;
56 /* Zip data structures */
58 /* compression methods */
59 #define STORE 0
61 struct local_file_header {
62 uint32_t signature;
63 uint16_t min_version;
64 uint16_t general_flag;
65 uint16_t compression;
66 uint16_t lastmod_time;
67 uint16_t lastmod_date;
68 uint32_t crc32;
69 uint32_t compressed_size;
70 uint32_t uncompressed_size;
71 uint16_t filename_size;
72 uint16_t extra_field_size;
73 char data[0];
74 } __attribute__((__packed__));
76 struct cdir_entry {
77 uint32_t signature;
78 uint16_t creator_version;
79 uint16_t min_version;
80 uint16_t general_flag;
81 uint16_t compression;
82 uint16_t lastmod_time;
83 uint16_t lastmod_date;
84 uint32_t crc32;
85 uint32_t compressed_size;
86 uint32_t uncompressed_size;
87 uint16_t filename_size;
88 uint16_t extra_field_size;
89 uint16_t file_comment_size;
90 uint16_t disk_num;
91 uint16_t internal_attr;
92 uint32_t external_attr;
93 uint32_t offset;
94 char data[0];
95 } __attribute__((__packed__));
97 #define CDIR_END_SIG 0x06054b50
99 struct cdir_end {
100 uint32_t signature;
101 uint16_t disk_num;
102 uint16_t cdir_disk;
103 uint16_t disk_entries;
104 uint16_t cdir_entries;
105 uint32_t cdir_size;
106 uint32_t cdir_offset;
107 uint16_t comment_size;
108 char comment[0];
109 } __attribute__((__packed__));
111 /* End of Zip data structures */
113 static struct cdir_entry *cdir_start;
114 static uint16_t cdir_entries;
116 /* Data structure to turn Zip's list in arbitrary order of
117 * hierarchical pathnames (not necessarily including entries for
118 * directories) into an actual hierarchical directory tree, so that we
119 * can iterate over directory entries properly in the dirent style
120 * functions.
123 typedef struct direntry *direntry;
125 struct direntry {
126 UT_hash_handle hh;
127 enum { REGULAR, DIRECTORY } kind;
128 int ino;
129 union {
130 struct cdir_entry *file;
131 direntry subdir;
135 struct lo_apk_dir {
136 direntry cur;
139 static direntry assets = NULL;
141 static uint32_t
142 cdir_entry_size(struct cdir_entry *entry)
144 return sizeof(*entry) +
145 letoh16(entry->filename_size) +
146 letoh16(entry->extra_field_size) +
147 letoh16(entry->file_comment_size);
151 setup_cdir(void)
153 struct cdir_end *dirend = (struct cdir_end *)((char *) apk_file + apk_file_size - sizeof(*dirend));
154 uint32_t cdir_offset;
156 while ((void *)dirend > apk_file &&
157 letoh32(dirend->signature) != CDIR_END_SIG)
158 dirend = (struct cdir_end *)((char *)dirend - 1);
159 if (letoh32(dirend->signature) != CDIR_END_SIG) {
160 LOGE("setup_cdir: Could not find end of central directory record");
161 return 0;
164 cdir_offset = letoh32(dirend->cdir_offset);
166 cdir_entries = letoh16(dirend->cdir_entries);
167 cdir_start = (struct cdir_entry *)((char *)apk_file + cdir_offset);
169 return 1;
172 static struct cdir_entry *
173 find_cdir_entry(struct cdir_entry *entry, int count, const char *name)
175 size_t name_size = strlen(name);
176 while (count--) {
177 if (letoh16(entry->filename_size) == name_size &&
178 !memcmp(entry->data, name, name_size))
179 return entry;
180 entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
182 return NULL;
185 static void
186 handle_one_asset(struct cdir_entry *entry)
188 /* In the .apk there are no initial slashes */
189 const char *p = entry->data + sizeof("assets/")-1;
190 const char *z = entry->data + entry->filename_size;
191 direntry *dir = &assets;
192 static int ino = 1;
194 while (p < z) {
195 const char *q = p;
196 direntry old, new;
198 while (q < z && *q != '/')
199 q++;
200 HASH_FIND(hh, *dir, p, (unsigned)(q - p), old);
201 if (*q == '/') {
202 if (old == NULL) {
203 new = malloc(sizeof(*new));
204 new->ino = ino++;
205 new->kind = DIRECTORY;
206 new->subdir = NULL;
207 HASH_ADD_KEYPTR(hh, *dir, p, (unsigned)(q - p), new);
208 dir = &new->subdir;
209 } else {
210 dir = &old->subdir;
212 p = q + 1;
213 } else {
214 if (old == NULL) {
215 new = malloc(sizeof(*new));
216 new->ino = ino++;
217 new->kind = REGULAR;
218 new->file = entry;
219 HASH_ADD_KEYPTR(hh, *dir, p, (unsigned)(q - p), new);
220 } else {
221 LOGE("duplicate entry in apk: %.*s", entry->filename_size, entry->data);
223 p = q;
225 (void) dir;
230 setup_assets_tree(void)
232 int count = cdir_entries;
233 struct cdir_entry *entry = cdir_start;
235 while (count--) {
236 if (letoh16(entry->filename_size) >= sizeof("assets/")-1 &&
237 memcmp(entry->data, "assets/", sizeof("assets/")-1) == 0)
238 handle_one_asset(entry);
239 entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
241 return 1;
244 /* The lo-native-code shared library is always loaded from Java, so this is
245 * always called by JNI first.
247 __attribute__ ((visibility("default")))
248 jint
249 JNI_OnLoad(JavaVM* vm, void* reserved)
251 (void) reserved;
253 the_java_vm = vm;
255 return JNI_VERSION_1_6;
258 // public static native boolean setup(String dataDir,
259 // String cacheDir,
260 // String apkFile)
262 __attribute__ ((visibility("default")))
263 jboolean
264 Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2
265 (JNIEnv* env,
266 jobject clazz,
267 jstring dataDir,
268 jstring cacheDir,
269 jstring apkFile)
271 struct stat st;
272 int fd;
273 const char *dataDirPath;
274 const char *cacheDirPath;
275 const char *apkFilePath;
277 (void) clazz;
279 dataDirPath = (*env)->GetStringUTFChars(env, dataDir, NULL);
280 data_dir = strdup(dataDirPath);
281 (*env)->ReleaseStringUTFChars(env, dataDir, dataDirPath);
283 cacheDirPath = (*env)->GetStringUTFChars(env, cacheDir, NULL);
284 cache_dir = strdup(cacheDirPath);
285 (*env)->ReleaseStringUTFChars(env, cacheDir, cacheDirPath);
287 apkFilePath = (*env)->GetStringUTFChars(env, apkFile, NULL);
289 fd = open(apkFilePath, O_RDONLY);
290 if (fd == -1) {
291 LOGE("Could not open %s", apkFilePath);
292 (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath);
293 return JNI_FALSE;
295 if (fstat(fd, &st) == -1) {
296 LOGE("Could not fstat %s", apkFilePath);
297 close(fd);
298 (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath);
299 return JNI_FALSE;
301 apk_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
302 close(fd);
304 if (apk_file == MAP_FAILED) {
305 LOGE("Could not mmap %s", apkFilePath);
306 (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath);
307 return JNI_FALSE;
309 apk_file_size = st.st_size;
311 (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath);
313 if (!setup_cdir())
314 return JNI_FALSE;
316 if (!setup_assets_tree())
317 return JNI_FALSE;
319 return JNI_TRUE;
322 static jboolean
323 get_jni_string_array(JNIEnv *env,
324 const char *function_and_parameter_name,
325 jobject strv,
326 int *argc,
327 const char ***argv)
329 jclass StringArray;
330 int i;
332 StringArray = (*env)->FindClass(env, "[Ljava/lang/String;");
333 if (StringArray == NULL) {
334 LOGE("Could not find String[] class");
335 return JNI_FALSE;
338 if (!(*env)->IsInstanceOf(env, strv, StringArray)) {
339 LOGE("%s is not a String[]?", function_and_parameter_name);
340 return JNI_FALSE;
343 *argc = (*env)->GetArrayLength(env, strv);
344 *argv = malloc(sizeof(char *) * (*argc+1));
346 for (i = 0; i < *argc; i++) {
347 const char *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, strv, i), NULL);
348 (*argv)[i] = strdup(s);
349 (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, strv, i), s);
351 (*argv)[*argc] = NULL;
353 return JNI_TRUE;
356 // public static native int getpid();
358 __attribute__ ((visibility("default")))
359 jint
360 Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv* env,
361 jobject clazz)
363 (void) env;
364 (void) clazz;
366 return getpid();
369 // public static native void system(String cmdline);
371 __attribute__ ((visibility("default")))
372 void
373 Java_org_libreoffice_android_Bootstrap_system(JNIEnv* env,
374 jobject clazz,
375 jstring cmdline)
377 const char *s;
379 (void) clazz;
381 s = (*env)->GetStringUTFChars(env, cmdline, NULL);
383 LOGI("system(%s)", s);
385 system(s);
387 (*env)->ReleaseStringUTFChars(env, cmdline, s);
390 // public static native void putenv(String string);
392 __attribute__ ((visibility("default")))
393 void
394 Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env,
395 jobject clazz,
396 jstring string)
398 const char *s;
399 char *s_copy;
401 (void) clazz;
403 s = (*env)->GetStringUTFChars(env, string, NULL);
404 s_copy = strdup(s);
406 LOGI("putenv(%s)", s_copy);
408 putenv(s_copy);
410 #if 0
412 static int beenhere=0;
413 if (!beenhere) {
414 LOGI("lo-bootstrap: Sleeping for 20 seconds, start ndk-gdb NOW if that is your intention");
415 sleep(20);
416 beenhere = 1;
419 #endif
421 (*env)->ReleaseStringUTFChars(env, string, s);
424 __attribute__ ((visibility("default")))
425 void *
426 lo_apkentry(const char *filename,
427 size_t *size)
429 struct cdir_entry *entry;
430 struct local_file_header *file;
431 void *data;
433 if (*filename == '/')
434 filename++;
436 entry = find_cdir_entry(cdir_start, cdir_entries, filename);
438 if (entry == NULL) {
439 LOGE("lo_apkentry: Could not find %s", filename);
440 return NULL;
442 file = (struct local_file_header *)((char *)apk_file + letoh32(entry->offset));
444 if (letoh16(file->compression) != STORE) {
445 LOGE("lo_apkentry: File %s is compressed", filename);
446 return NULL;
449 data = ((char *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
450 *size = file->uncompressed_size;
452 /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */
454 return data;
457 __attribute__ ((visibility("default")))
458 lo_apk_dir *
459 lo_apk_opendir(const char *dirname)
461 /* In the .apk there are no initial slashes, but the parameter passed to
462 * us does have it.
464 const char *p = dirname + sizeof("/assets/")-1;
465 direntry dir = assets;
467 if (!*p) {
468 lo_apk_dir *result = malloc(sizeof(*result));
469 result->cur = assets;
470 return result;
473 while (1) {
474 const char *q = p;
475 direntry entry;
477 while (*q && *q != '/')
478 q++;
480 HASH_FIND(hh, dir, p, (unsigned)(q - p), entry);
482 if (entry == NULL && *q == '/') {
483 errno = ENOENT;
484 return NULL;
485 } else if (entry == NULL) {
486 /* Empty directories, or directories containing only "hidden"
487 * files (like the .gitignore in sc/qa/unit/qpro/indeterminate)
488 * are not present in the .apk. So we need to pretend that any
489 * directory that doesn't exist as a parent of an entry in the
490 * .apk *does* exist but is empty.
492 lo_apk_dir *result = malloc(sizeof(*result));
493 result->cur = NULL;
494 return result;
497 if (entry->kind != DIRECTORY) {
498 errno = ENOTDIR;
499 return NULL;
502 if (!q[0] || !q[1]) {
503 lo_apk_dir *result = malloc(sizeof(*result));
504 result->cur = entry->subdir;
505 return result;
508 dir = entry->subdir;
509 p = q + 1;
513 __attribute__ ((visibility("default")))
514 struct dirent *
515 lo_apk_readdir(lo_apk_dir *dirp)
517 static struct dirent result;
519 if (dirp->cur == NULL) {
520 /* LOGI("lo_apk_readdir(%p) = NULL", dirp); */
521 return NULL;
524 result.d_ino = dirp->cur->ino;
525 result.d_off = 0;
526 result.d_reclen = 0;
528 if (dirp->cur->kind == DIRECTORY)
529 result.d_type = DT_DIR;
530 else
531 result.d_type = DT_REG;
533 memcpy(result.d_name, dirp->cur->hh.key, dirp->cur->hh.keylen);
534 result.d_name[dirp->cur->hh.keylen] = '\0';
536 dirp->cur = dirp->cur->hh.next;
538 /* LOGI("lo_apk_readdir(%p) = %s:%s", dirp, result.d_type == DT_DIR ? "DIR" : "REG", result.d_name); */
540 return &result;
543 __attribute__ ((visibility("default")))
545 lo_apk_closedir(lo_apk_dir *dirp)
547 free(dirp);
549 /* LOGI("lo_apk_closedir(%p)", dirp); */
551 return 0;
554 static int
555 new_stat(const char *path,
556 struct stat *statp,
557 struct cdir_entry *entry,
558 int mode,
559 int fake_ino)
561 struct tm tm;
563 memset(statp, 0, sizeof(*statp));
564 statp->st_mode = mode | S_IRUSR;
565 statp->st_nlink = 1;
567 statp->st_uid = getuid();
568 statp->st_gid = getgid();
570 if (entry != NULL)
571 statp->st_size = entry->uncompressed_size;
572 else
573 statp->st_size = 0;
574 statp->st_blksize = 512;
575 if (statp->st_size == 0)
576 statp->st_blocks = 0;
577 else
578 statp->st_blocks = (statp->st_size - 1) / statp->st_blksize + 1;
580 statp->st_atime = time(NULL);
582 memset(&tm, 0, sizeof(tm));
583 tm.tm_sec = (letoh16(entry->lastmod_time) & 0x1F) * 2;
584 tm.tm_min = (letoh16(entry->lastmod_time) >> 5) & 0x3F;
585 tm.tm_hour = (letoh16(entry->lastmod_time) >> 11) & 0x1F;
586 tm.tm_mday = letoh16(entry->lastmod_date) & 0x1F;
587 tm.tm_mon = ((letoh16(entry->lastmod_date) >> 5) & 0x0F) - 1;
588 tm.tm_year = ((letoh16(entry->lastmod_date) >> 9) & 0x7F) + 80;
590 statp->st_mtime = mktime(&tm);
591 statp->st_ctime = statp->st_mtime;
593 statp->st_ino = fake_ino;
595 (void) path;
596 /* LOGI("lo_apk_lstat(%s) = { mode=%o, size=%lld, ino=%lld mtime=%.24s }",
597 path, statp->st_mode, statp->st_size, statp->st_ino,
598 ctime((const time_t *) &statp->st_mtime)); */
600 return 0;
603 __attribute__ ((visibility("default")))
605 lo_apk_lstat(const char *path,
606 struct stat *statp)
608 const char *pn = path;
609 int count = cdir_entries;
610 struct cdir_entry *entry = cdir_start;
611 size_t name_size;
613 if (*pn == '/') {
614 pn++;
615 if (!pn[0])
616 return new_stat(path, statp, NULL, S_IFDIR | S_IXUSR, 1);
619 name_size = strlen(pn);
620 while (count--)
622 if (letoh16(entry->filename_size) >= name_size &&
623 !memcmp(entry->data, pn, name_size) &&
624 (letoh16(entry->filename_size) == name_size || entry->data[name_size] == '/'))
625 break;
626 entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
628 if (count >= 0) {
629 if (letoh16(entry->filename_size) == name_size)
630 return new_stat(path, statp, entry, S_IFREG, cdir_entries - count + 1);
631 else
632 return new_stat(path, statp, entry, S_IFDIR | S_IXUSR, cdir_entries - count + 1);
635 errno = ENOENT;
636 return -1;
639 /* Android's JNI works only to libraries loaded through Java's
640 * System.loadLibrary(), it seems. But now with just one big app-specific .so
641 * on Android, that would not be a problem, but for historical reasons, we
642 * have JNI wrappers here, and then call the VCL etc function from them. Oh
643 * well, one could say it's clean to have all the Android-specific JNI
644 * functions here in this file.
647 extern void osl_setCommandArgs(int, char **);
649 __attribute__ ((visibility("default")))
650 void
651 Java_org_libreoffice_android_Bootstrap_setCommandArgs(JNIEnv* env,
652 jobject clazz,
653 jobject argv)
655 char **c_argv;
656 int c_argc;
657 Dl_info lo_bootstrap_info;
659 (void) clazz;
661 if (!get_jni_string_array(env, "setCommandArgs :argv", argv, &c_argc, (const char ***) &c_argv))
662 return;
664 if (dladdr(Java_org_libreoffice_android_Bootstrap_setCommandArgs, &lo_bootstrap_info) != 0) {
665 char *new_argv0 = malloc(strlen(lo_bootstrap_info.dli_fname) + strlen(c_argv[0]));
666 char *slash;
667 strcpy(new_argv0, lo_bootstrap_info.dli_fname);
668 slash = strrchr(new_argv0, '/');
669 if (slash != NULL)
670 *slash = '\0';
671 slash = strrchr(new_argv0, '/');
672 if (slash != NULL)
673 strcpy(slash+1, c_argv[0]);
674 else
675 strcpy(new_argv0, c_argv[0]);
676 free(c_argv[0]);
677 c_argv[0] = new_argv0;
680 osl_setCommandArgs(c_argc, c_argv);
683 /* Code for reading lines from the pipe based on the (Apache-licensed) Android
684 * logwrapper.c
687 static int
688 read_from(int fd, const char *tag, char *buffer, int *sz, int *a, int *b, size_t sizeof_buffer)
690 int nread;
692 nread = read(fd, buffer+*b, sizeof_buffer - 1 - *b);
693 *sz = nread;
695 if (nread == -1) {
696 LOGE("redirect_thread: Reading from %d failed: %s", fd, strerror(errno));
697 close(fd);
698 return -1;
701 if (nread == 0) {
702 LOGI("redirect_thread: EOF from fd %d", fd);
703 close(fd);
704 return 0;
707 *sz += *b;
709 for (*b = 0; *b < *sz; (*b)++) {
710 if (buffer[*b] == '\n') {
711 buffer[*b] = '\0';
712 __android_log_print(ANDROID_LOG_INFO, tag, "%s", &buffer[*a]);
713 *a = *b + 1;
717 if (*a == 0 && *b == (int) sizeof_buffer - 1) {
718 // buffer is full, flush
719 buffer[*b] = '\0';
720 __android_log_print(ANDROID_LOG_INFO, tag, "%s", &buffer[*a]);
721 *b = 0;
722 } else if (*a != *b) {
723 // Keep left-overs
724 *b -= *a;
725 memmove(buffer, &buffer[*a], *b);
726 *a = 0;
727 } else {
728 *a = 0;
729 *b = 0;
732 return nread;
735 static int stdout_pipe[2], stderr_pipe[2];
737 static void *
738 redirect_thread(void *arg)
740 char buffer[2][4096];
741 int a[2] = { 0, 0 };
742 int b[2] = { 0, 0 };
743 int sz[2];
745 (void) arg;
747 while (1) {
748 fd_set readfds;
749 int nfds = 0;
751 FD_ZERO(&readfds);
752 if (stdout_pipe[0] != -1) {
753 FD_SET(stdout_pipe[0], &readfds);
754 nfds = MAX(nfds, stdout_pipe[0] + 1);
756 if (stderr_pipe[0] != -1) {
757 FD_SET(stderr_pipe[0], &readfds);
758 nfds = MAX(nfds, stderr_pipe[0] + 1);
760 if (nfds == 0) {
761 LOGI("redirect_thread: Nothing to read any more, thread exiting");
762 return NULL;
765 if (select(nfds, &readfds, NULL, NULL, NULL) == -1) {
766 LOGE("redirect_thread: select failed: %s, thread exiting", strerror(errno));
767 close(stdout_pipe[0]);
768 stdout_pipe[0] = -1;
769 close(stderr_pipe[0]);
770 stderr_pipe[0] = -1;
771 return NULL;
774 if (stdout_pipe[0] != -1 &&
775 FD_ISSET(stdout_pipe[0], &readfds)) {
776 if (read_from(stdout_pipe[0], "stdout", buffer[0], &sz[0], &a[0], &b[0], sizeof(buffer[0])) <= 0) {
777 stdout_pipe[0] = -1;
781 if (stderr_pipe[0] != -1 &&
782 FD_ISSET(stderr_pipe[0], &readfds)) {
783 if (read_from(stderr_pipe[0], "stderr", buffer[1], &sz[1], &a[1], &b[1], sizeof(buffer[1])) <= 0) {
784 stderr_pipe[0] = -1;
790 static int
791 redirect_to_null(void)
793 int null = open("/dev/null", O_WRONLY);
794 if (null == -1) {
795 LOGE("redirect_stdio: Could not open /dev/null: %s", strerror(errno));
796 /* If we can't redirect stdout or stderr to /dev/null, just close them
797 * then instead. Huh?
799 close(1);
800 close(2);
801 return 0;
803 if (dup2(null, 1) == -1) {
804 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", null, strerror(errno));
805 close(null);
806 close(1);
807 close(2);
808 return 0;
810 if (dup2(null, 2) == -1) {
811 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", null, strerror(errno));
812 close(null);
813 close(1);
814 close(2);
815 return 0;
817 close(null);
818 return 1;
821 __attribute__ ((visibility("default")))
822 void
823 Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv* env,
824 jobject clazz,
825 jboolean state)
827 static jboolean current = JNI_FALSE;
828 pthread_t thread;
829 pthread_attr_t attr;
831 (void) env;
832 (void) clazz;
834 if (state == current)
835 return;
837 if (state == JNI_FALSE) {
838 if (!redirect_to_null())
839 return;
840 } else {
841 if (pipe(stdout_pipe) == -1) {
842 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno));
843 return;
845 if (pipe(stderr_pipe) == -1) {
846 LOGE("redirect_stdio: Could not create pipes: %s", strerror(errno));
847 close(stdout_pipe[0]);
848 close(stdout_pipe[1]);
849 return;
851 LOGI("redirect_stdio: stdout pipe: [%d,%d], stderr pipe: [%d,%d]",
852 stdout_pipe[0], stdout_pipe[1], stderr_pipe[0], stderr_pipe[1]);
854 if (dup2(stdout_pipe[1], 1) == -1) {
855 LOGE("redirect_stdio: Could not dup2 %d to 1: %s", stdout_pipe[1], strerror(errno));
856 close(stdout_pipe[0]);
857 close(stdout_pipe[1]);
858 close(stderr_pipe[0]);
859 close(stderr_pipe[1]);
860 return;
863 if (dup2(stderr_pipe[1], 2) == -1) {
864 LOGE("redirect_stdio: Could not dup2 %d to 2: %s", stdout_pipe[1], strerror(errno));
865 /* stdout has already been redirected to its pipe, so redirect
866 * it back to /dev/null
868 redirect_to_null();
869 close(stdout_pipe[0]);
870 close(stdout_pipe[1]);
871 close(stderr_pipe[0]);
872 close(stderr_pipe[1]);
873 return;
875 close(stdout_pipe[1]);
876 close(stderr_pipe[1]);
878 if (pthread_attr_init(&attr) != 0 ||
879 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0 ||
880 pthread_create(&thread, &attr, redirect_thread, NULL) != 0) {
881 LOGE("redirect_stdio: Could not create thread: %s", strerror(errno));
882 redirect_to_null();
883 close(stdout_pipe[0]);
884 close(stderr_pipe[0]);
885 return;
888 current = state;
889 return;
892 __attribute__ ((visibility("default")))
893 jlong
894 Java_org_libreoffice_android_Bootstrap_address_1of_1direct_1byte_1buffer(JNIEnv *env,
895 jobject bbuffer)
897 return (jlong) (intptr_t) (*env)->GetDirectBufferAddress(env, bbuffer);
900 __attribute__ ((visibility("default")))
901 void
902 libreofficekit_set_javavm(JavaVM *vm)
904 the_java_vm = vm;
907 __attribute__ ((visibility("default")))
908 JavaVM *
909 lo_get_javavm(void)
911 return the_java_vm;
914 __attribute__ ((visibility("default")))
915 const char *
916 lo_get_app_data_dir(void)
918 return data_dir;
921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */