2 * ptm - CUSE based TPM PassThrough Multiplexer for QEMU.
4 * This program instantiates one /dev/vtpm* device, and
5 * calls libtpms to handle requests
7 * The following code was derived from
8 * http://fuse.sourceforge.net/doxygen/cusexmp_8c.html
10 * It's original header states:
12 * CUSE example: Character device in Userspace
13 * Copyright (C) 2008-2009 SUSE Linux Products GmbH
14 * Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
15 * This program can be distributed under the terms of the GNU GPL.
16 * See the file COPYING.
19 * Authors: David Safford safford@us.ibm.com
20 * Stefan Berger stefanb@us.ibm.com
25 * Note: It's possible for multiple process to open access to
26 * the same character device. Concurrency problems may arise
27 * if those processes all write() to the device and then try
28 * to pick up the results. Proper usage of the device is to
29 * have one process (QEMU) use ioctl, read and write and have
30 * other processes (libvirt, etc.) only use ioctl.
32 #define FUSE_USE_VERSION 29
42 #include <sys/types.h>
46 #include <arpa/inet.h>
48 #include <libtpms/tpm_library.h>
49 #include <libtpms/tpm_tis.h>
50 #include <libtpms/tpm_error.h>
51 #include <libtpms/tpm_memory.h>
52 #include <libtpms/tpm_nvfilename.h>
54 #include "cuse_lowlevel.h"
56 #include "tpm_ioctl.h"
58 #include "swtpm_nvfile.h"
66 #define TPM_REQ_MAX 4096
67 static unsigned char *ptm_req
, *ptm_res
;
68 static uint32_t ptm_req_len
, ptm_res_len
, ptm_res_tot
;
69 static TPM_MODIFIER_INDICATOR locality
;
70 static int tpm_running
;
71 static int thread_busy
;
72 static GThreadPool
*pool
;
73 static struct passwd
*passwd
;
75 #if GLIB_MAJOR_VERSION >= 2
76 # if GLIB_MINOR_VERSION >= 32
78 GCond thread_busy_signal
;
79 GMutex thread_busy_lock
;
81 # define THREAD_BUSY_SIGNAL &thread_busy_signal
82 # define THREAD_BUSY_LOCK &thread_busy_lock
83 # define FILE_OPS_LOCK &file_ops_lock
87 GCond
*thread_busy_signal
;
88 GMutex
*thread_busy_lock
;
89 GMutex
*file_ops_lock
;
90 # define THREAD_BUSY_SIGNAL thread_busy_signal
91 # define THREAD_BUSY_LOCK thread_busy_lock
92 # define FILE_OPS_LOCK file_ops_lock
97 #error Unsupport glib version
118 struct thread_message
{
123 #define min(a,b) ((a) < (b) ? (a) : (b))
131 static const char *usage
=
132 "usage: %s [options]\n"
134 "The following options are supported:\n"
136 "-n NAME|--name=NAME : device name (mandatory)\n"
137 "-M MAJ|--maj=MAJ : device major number\n"
138 "-m MIN|--min=MIN : device minor number\n"
139 "--key file=<path>[,mode=aes-cbc][,format=hex|binary][,remove=[true|false]]\n"
140 " : use an AES key for the encryption of the TPM's state\n"
141 " files; use the given mode for the block encryption;\n"
142 " the key is to be provided as a hex string or in binary\n"
143 " format; the keyfile can be automatically removed using\n"
144 " the remove parameter\n"
145 "--key pwdfile=<path>[,mode=aes-cbc][,remove=[true|false]]\n"
146 " : provide a passphrase in a file; the AES key will be\n"
147 " derived from this passphrase\n"
148 "--log file=<path>|fd=<filedescriptor>\n"
149 " : write the TPM's log into the given file rather than\n"
150 " to the console; provide '-' for path to avoid logging\n"
151 "-h|--help : display this help screen and terminate\n"
153 "Make sure that TPM_PATH environment variable points to directory\n"
154 "where TPM's NV storage file is kept\n"
157 const static unsigned char TPM_Resp_FatalError
[] = {
158 0x00, 0xC4, /* TPM Response */
159 0x00, 0x00, 0x00, 0x0A, /* length (10) */
160 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */
163 const static unsigned char TPM_ResetEstablishmentBit
[] = {
164 0x00, 0xC1, /* TPM Request */
165 0x00, 0x00, 0x00, 0x0A, /* length (10) */
166 0x40, 0x00, 0x00, 0x0B /* TPM_ORD_ResetEstablishmentBit */
169 typedef struct TPM_Response_Header
{
173 } __attribute__ ((packed
)) TPM_Response_Header
;
176 ptm_io_getlocality(TPM_MODIFIER_INDICATOR
*loc
, uint32_t tpmnum
)
182 static struct libtpms_callbacks cbs
= {
183 .sizeOfStruct
= sizeof(struct libtpms_callbacks
),
184 .tpm_nvram_init
= SWTPM_NVRAM_Init
,
185 .tpm_nvram_loaddata
= SWTPM_NVRAM_LoadData
,
186 .tpm_nvram_storedata
= SWTPM_NVRAM_StoreData
,
187 .tpm_nvram_deletename
= SWTPM_NVRAM_DeleteName
,
188 .tpm_io_getlocality
= ptm_io_getlocality
,
191 static struct thread_message msg
;
193 /* worker_thread_wait_done
195 * Wait while the TPM worker thread is busy
197 static void worker_thread_wait_done(void)
199 g_mutex_lock(THREAD_BUSY_LOCK
);
200 while (thread_busy
) {
201 #if GLIB_MINOR_VERSION >= 32
202 gint64 end_time
= g_get_monotonic_time() +
203 1 * G_TIME_SPAN_SECOND
;
204 g_cond_wait_until(THREAD_BUSY_SIGNAL
,
210 * seems like occasionally the g_cond_signal did not wake up
211 * the sleeping task; so we poll [TIS Test in BIOS]
214 abs_time
.tv_usec
= 0;
215 g_cond_timed_wait(THREAD_BUSY_SIGNAL
,
220 g_mutex_unlock(THREAD_BUSY_LOCK
);
223 /* worker_thread_mark_busy
225 * Mark the worker thread as busy; call this with the lock held
227 static void worker_thread_mark_busy(void)
229 g_mutex_lock(THREAD_BUSY_LOCK
);
231 g_mutex_unlock(THREAD_BUSY_LOCK
);
234 /* work_tread_mark_done
236 * Mark the worker thread as done and wake
237 * up the waiting thread
239 static void worker_thread_mark_done(void)
241 g_mutex_lock(THREAD_BUSY_LOCK
);
243 g_cond_signal(THREAD_BUSY_SIGNAL
);
244 g_mutex_unlock(THREAD_BUSY_LOCK
);
247 /* worker_thread_is_busy
249 * Determine whether the worker thread is busy
251 static int worker_thread_is_busy()
256 static void worker_thread(gpointer data
, gpointer user_data
)
258 struct thread_message
*msg
= (struct thread_message
*)data
;
261 case MESSAGE_TPM_CMD
:
262 TPMLIB_Process(&ptm_res
, &ptm_res_len
, &ptm_res_tot
,
263 ptm_req
, ptm_req_len
);
269 /* results are ready */
270 worker_thread_mark_done();
275 * finish the worker thread
277 static void worker_thread_end()
280 worker_thread_wait_done();
281 g_thread_pool_free(pool
, TRUE
, TRUE
);
286 /* _TPM_IO_TpmEstablished_Reset
288 * Reset the TPM Established bit
291 _TPM_IO_TpmEstablished_Reset(fuse_req_t req
,
292 TPM_MODIFIER_INDICATOR locty
)
294 TPM_RESULT res
= TPM_FAIL
;
295 TPM_Response_Header
*tpmrh
;
296 TPM_MODIFIER_INDICATOR orig_locality
= locality
;
300 ptm_req_len
= sizeof(TPM_ResetEstablishmentBit
);
301 memcpy(ptm_req
, TPM_ResetEstablishmentBit
, ptm_req_len
);
302 msg
.type
= MESSAGE_TPM_CMD
;
305 worker_thread_mark_busy();
307 g_thread_pool_push(pool
, &msg
, NULL
);
309 worker_thread_wait_done();
311 if (ptm_res_len
>= sizeof(TPM_Response_Header
)) {
312 tpmrh
= (TPM_Response_Header
*)ptm_res
;
313 res
= ntohl(tpmrh
->returnCode
);
316 locality
= orig_locality
;
321 static int tpm_start(uint32_t flags
)
324 char * tpmdir
= NULL
;
326 /* temporary - the backend script lacks the perms to do this */
327 if (tpmdir
== NULL
) {
328 tpmdir
= getenv("TPM_PATH");
330 logprintf(STDOUT_FILENO
,
331 "Error: TPM_PATH is not set\n");
335 dir
= opendir(tpmdir
);
339 if (mkdir(tpmdir
, 0775)) {
340 logprintf(STDERR_FILENO
,
341 "Error: Could not open TPM_PATH dir\n");
346 pool
= g_thread_pool_new(worker_thread
,
352 logprintf(STDERR_FILENO
,
353 "Error: Could not create the thread pool.\n");
357 if (TPMLIB_RegisterCallbacks(&cbs
) != TPM_SUCCESS
) {
358 logprintf(STDERR_FILENO
,
359 "Error: Could not register the callbacks.\n");
363 if (TPMLIB_MainInit() != TPM_SUCCESS
) {
364 logprintf(STDERR_FILENO
,
365 "Error: Could not start the CUSE TPM.\n");
369 if (flags
& INIT_FLAG_DELETE_VOLATILE
) {
370 uint32_t tpm_number
= 0;
371 char *name
= TPM_VOLATILESTATE_NAME
;
372 if (SWTPM_NVRAM_DeleteName(tpm_number
,
374 FALSE
) != TPM_SUCCESS
) {
375 logprintf(STDERR_FILENO
,
376 "Error: Could not delete the volatile "
377 "state of the TPM.\n");
378 goto error_terminate
;
383 ptm_req
= malloc(4096);
385 logprintf(STDERR_FILENO
,
386 "Error: Could not allocate memory for request buffer.\n");
387 goto error_terminate
;
390 logprintf(STDOUT_FILENO
,
391 "CUSE TPM successfully initialized.\n");
396 g_thread_pool_free(pool
, TRUE
, TRUE
);
405 * convert the blobtype integer into a string that libtpms
408 static const char *ptm_get_blobname(uint8_t blobtype
)
411 case PTM_BLOB_TYPE_PERMANENT
:
412 return TPM_PERMANENT_ALL_NAME
;
413 case PTM_BLOB_TYPE_VOLATILE
:
414 return TPM_VOLATILESTATE_NAME
;
415 case PTM_BLOB_TYPE_SAVESTATE
:
416 return TPM_SAVESTATE_NAME
;
422 static void ptm_open(fuse_req_t req
, struct fuse_file_info
*fi
)
424 fuse_reply_open(req
, fi
);
427 /* ptm_write_fatal_error_response
429 * Write a fatal error response
431 static void ptm_write_fatal_error_response(void)
433 if (ptm_res
== NULL
||
434 ptm_res_tot
< sizeof(TPM_Resp_FatalError
)) {
435 ptm_res_tot
= sizeof(TPM_Resp_FatalError
);
436 TPM_Realloc(&ptm_res
, ptm_res_tot
);
439 ptm_res_len
= sizeof(TPM_Resp_FatalError
);
442 sizeof(TPM_Resp_FatalError
));
446 static void ptm_read(fuse_req_t req
, size_t size
, off_t off
,
447 struct fuse_file_info
*fi
)
452 /* wait until results are ready */
453 worker_thread_wait_done();
458 if (ptm_res_len
> size
) {
465 fuse_reply_buf(req
, (const char *)ptm_res
, len
);
468 static void ptm_write(fuse_req_t req
, const char *buf
, size_t size
,
469 off_t off
, struct fuse_file_info
*fi
)
474 /* prevent other threads from writing or doing ioctls */
475 g_mutex_lock(FILE_OPS_LOCK
);
478 /* ensure that we only ever work on one TPM command */
479 if (worker_thread_is_busy()) {
480 fuse_reply_err(req
, EBUSY
);
484 /* have command processed by thread pool */
485 if (ptm_req_len
> TPM_REQ_MAX
)
486 ptm_req_len
= TPM_REQ_MAX
;
488 memcpy(ptm_req
, buf
, ptm_req_len
);
489 msg
.type
= MESSAGE_TPM_CMD
;
492 worker_thread_mark_busy();
494 g_thread_pool_push(pool
, &msg
, NULL
);
496 fuse_reply_write(req
, ptm_req_len
);
498 /* TPM not initialized; return error */
499 ptm_write_fatal_error_response();
500 fuse_reply_write(req
, ptm_req_len
);
504 g_mutex_unlock(FILE_OPS_LOCK
);
510 * ptm_ioctl : ioctl execution
512 * req: the fuse_req_t used to send response with
513 * cmd: the ioctl request code
514 * arg: the pointer the application used for calling the ioctl (3rd param)
516 * flags: some flags provided by fuse
517 * in_buf: the copy of the input buffer
518 * in_bufsz: size of the input buffer; provided by fuse and has size of
520 * out_bufsz: size of the output buffer; provided by fuse and has size of
523 static void ptm_ioctl(fuse_req_t req
, int cmd
, void *arg
,
524 struct fuse_file_info
*fi
, unsigned flags
,
525 const void *in_buf
, size_t in_bufsz
, size_t out_bufsz
)
528 bool exit_prg
= FALSE
;
530 static struct stateblob stateblob
;
532 if (flags
& FUSE_IOCTL_COMPAT
) {
533 fuse_reply_err(req
, ENOSYS
);
537 /* some commands have to wait until the worker thread is done */
539 case PTM_GET_CAPABILITY
:
540 case PTM_SET_LOCALITY
:
541 case PTM_CANCEL_TPM_CMD
:
542 /* no need to wait */
546 case PTM_GET_TPMESTABLISHED
:
547 case PTM_RESET_TPMESTABLISHED
:
551 case PTM_STORE_VOLATILE
:
552 case PTM_GET_STATEBLOB
:
553 case PTM_SET_STATEBLOB
:
555 worker_thread_wait_done();
559 /* prevent other threads from writing or doing ioctls */
560 g_mutex_lock(FILE_OPS_LOCK
);
563 case PTM_GET_CAPABILITY
:
565 struct iovec iov
= { arg
, sizeof(uint8_t) };
566 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
569 ptm_caps
= PTM_CAP_INIT
| PTM_CAP_SHUTDOWN
570 | PTM_CAP_GET_TPMESTABLISHED
571 | PTM_CAP_SET_LOCALITY
573 | PTM_CAP_CANCEL_TPM_CMD
574 | PTM_CAP_STORE_VOLATILE
575 | PTM_CAP_RESET_TPMESTABLISHED
576 | PTM_CAP_GET_STATEBLOB
577 | PTM_CAP_SET_STATEBLOB
579 fuse_reply_ioctl(req
, 0, &ptm_caps
, sizeof(ptm_caps
));
584 init_p
= (ptminit_t
*)in_buf
;
591 if ((res
= tpm_start(init_p
->u
.req
.init_flags
))) {
592 logprintf(STDERR_FILENO
,
593 "Error: Could not initialize the TPM.\n");
597 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
611 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
624 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
629 case PTM_GET_TPMESTABLISHED
:
631 goto error_not_running
;
634 struct iovec iov
= { arg
, sizeof(uint8_t) };
635 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
638 te
.tpm_result
= TPM_IO_TpmEstablished_Get(&te
.bit
);
639 fuse_reply_ioctl(req
, 0, &te
, sizeof(te
));
643 case PTM_RESET_TPMESTABLISHED
:
645 goto error_not_running
;
648 struct iovec iov
= { arg
, sizeof(uint32_t) };
649 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
651 ptmreset_est_t
*re
= (ptmreset_est_t
*)in_buf
;
652 if (re
->u
.req
.loc
> 4) {
653 res
= TPM_BAD_LOCALITY
;
655 res
= _TPM_IO_TpmEstablished_Reset(req
, re
->u
.req
.loc
);
656 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
661 case PTM_SET_LOCALITY
:
663 struct iovec iov
= { arg
, sizeof(uint32_t) };
664 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
666 ptmloc_t
*l
= (ptmloc_t
*)in_buf
;
667 if (l
->u
.req
.loc
> 4) {
668 res
= TPM_BAD_LOCALITY
;
671 locality
= l
->u
.req
.loc
;
673 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
679 goto error_not_running
;
681 res
= TPM_IO_Hash_Start();
682 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
687 goto error_not_running
;
690 struct iovec iov
= { arg
, sizeof(uint32_t) };
691 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
693 ptmhdata_t
*data
= (ptmhdata_t
*)in_buf
;
694 if (data
->u
.req
.length
<= sizeof(data
->u
.req
.data
)) {
695 res
= TPM_IO_Hash_Data(data
->u
.req
.data
,
700 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
706 goto error_not_running
;
708 res
= TPM_IO_Hash_End();
709 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
712 case PTM_CANCEL_TPM_CMD
:
714 goto error_not_running
;
716 /* for cancellation to work, the TPM would have to
717 * execute in another thread that polls on a cancel
721 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
724 case PTM_STORE_VOLATILE
:
726 goto error_not_running
;
728 res
= SWTPM_NVRAM_Store_Volatile();
729 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
732 case PTM_GET_STATEBLOB
:
734 goto error_not_running
;
737 struct iovec iov
= { arg
, sizeof(uint32_t) };
738 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
740 ptm_getstate_t
*pgs
= (ptm_getstate_t
*)in_buf
;
741 const char *blobname
= ptm_get_blobname(pgs
->u
.req
.type
);
742 unsigned char *data
= NULL
;
743 uint32_t length
= 0, to_copy
, offset
;
744 TPM_BOOL decrypt
= ((pgs
->u
.req
.state_flags
& STATE_FLAG_DECRYPTED
)
746 TPM_BOOL is_encrypted
;
747 uint32_t tpm_number
= pgs
->u
.req
.tpm_number
;
748 uint32_t blobtype
= pgs
->u
.req
.type
;
751 offset
= pgs
->u
.req
.offset
;
753 res
= SWTPM_NVRAM_GetStateBlob(&data
, &length
,
757 if (data
!= NULL
&& length
> 0) {
759 if (offset
< length
) {
760 to_copy
= min(length
- offset
,
761 sizeof(pgs
->u
.resp
.data
));
762 memcpy(&pgs
->u
.resp
.data
, &data
[offset
], to_copy
);
765 pgs
->u
.resp
.length
= to_copy
;
769 pgs
->u
.resp
.state_flags
= 0;
771 pgs
->u
.resp
.state_flags
|= STATE_FLAG_ENCRYPTED
;
773 if (blobtype
== PTM_BLOB_TYPE_VOLATILE
&&
774 to_copy
< sizeof(pgs
->u
.resp
.data
)) {
775 /* volatile blob deleted once transferred */
776 SWTPM_NVRAM_DeleteName(tpm_number
, blobname
, FALSE
);
780 * blob presumably does not exist; not an error
781 * res would show TPM_RETRY (0x800) flag
783 pgs
->u
.resp
.length
= 0;
786 res
= TPM_BAD_PARAMETER
;
788 pgs
->u
.resp
.tpm_result
= res
;
789 fuse_reply_ioctl(req
, 0, pgs
, sizeof(pgs
->u
.resp
));
793 case PTM_SET_STATEBLOB
:
797 /* tpm state dir must be set */
801 struct iovec iov
= { arg
, sizeof(uint32_t) };
802 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
804 ptm_setstate_t
*pss
= (ptm_setstate_t
*)in_buf
;
805 const char *blobname
;
806 TPM_BOOL is_encrypted
=
807 ((pss
->u
.req
.state_flags
& STATE_FLAG_ENCRYPTED
) != 0);
809 if (pss
->u
.req
.length
> sizeof(pss
->u
.req
.data
)) {
810 pss
->u
.resp
.tpm_result
= TPM_BAD_PARAMETER
;
811 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
815 if (stateblob
.type
!= pss
->u
.req
.type
) {
817 TPM_Free(stateblob
.data
);
818 stateblob
.data
= NULL
;
819 stateblob
.length
= 0;
820 stateblob
.type
= pss
->u
.req
.type
;
824 res
= TPM_Realloc(&stateblob
.data
,
825 stateblob
.length
+ pss
->u
.req
.length
);
828 TPM_Free(stateblob
.data
);
829 stateblob
.data
= NULL
;
830 stateblob
.length
= 0;
833 pss
->u
.resp
.tpm_result
= res
;
834 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
838 memcpy(&stateblob
.data
[stateblob
.length
],
839 pss
->u
.req
.data
, pss
->u
.req
.length
);
840 stateblob
.length
+= pss
->u
.req
.length
;
842 if (pss
->u
.req
.length
== sizeof(pss
->u
.req
.data
)) {
844 pss
->u
.resp
.tpm_result
= 0;
845 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
848 blobname
= ptm_get_blobname(pss
->u
.req
.type
);
851 res
= SWTPM_NVRAM_SetStateBlob(stateblob
.data
,
854 pss
->u
.req
.tpm_number
,
857 res
= TPM_BAD_PARAMETER
;
859 TPM_Free(stateblob
.data
);
860 stateblob
.data
= NULL
;
861 stateblob
.length
= 0;
864 pss
->u
.resp
.tpm_result
= res
;
865 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
870 fuse_reply_err(req
, EINVAL
);
874 g_mutex_unlock(FILE_OPS_LOCK
);
877 logprintf(STDOUT_FILENO
,
878 "CUSE TPM is shutting down.\n");
886 res
= TPM_BAD_ORDINAL
;
887 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
892 static void ptm_init_done(void *userdata
) {
894 if (initgroups(passwd
->pw_name
, passwd
->pw_gid
) < 0) {
895 logprintf(STDERR_FILENO
,
896 "Error: initgroups(%s, %d) failed.\n",
897 passwd
->pw_name
, passwd
->pw_gid
);
900 if (setgid(passwd
->pw_gid
) < 0) {
901 logprintf(STDERR_FILENO
,
902 "Error: setgid(%d) failed.\n",
906 if (setuid(passwd
->pw_uid
) < 0) {
907 logprintf(STDERR_FILENO
,
908 "Error: setuid(%d) failed.\n",
915 static const struct cuse_lowlevel_ops ptm_clop
= {
920 .init_done
= ptm_init_done
,
923 #define PTM_OPT(t, p) { t, offsetof(struct ptm_param, p), 1 }
925 static const struct fuse_opt ptm_opts
[] = {
926 PTM_OPT("-M %u", major
),
927 PTM_OPT("--maj=%u", major
),
928 PTM_OPT("-m %u", minor
),
929 PTM_OPT("--min=%u", minor
),
930 PTM_OPT("-n %s", dev_name
),
931 PTM_OPT("--name=%s", dev_name
),
932 PTM_OPT("-r %s", runas
),
933 PTM_OPT("--runas=%s", runas
),
934 PTM_OPT("--log %s", logging
),
935 PTM_OPT("--key %s", keydata
),
936 FUSE_OPT_KEY("-h", 0),
937 FUSE_OPT_KEY("--help", 0),
938 FUSE_OPT_KEY("-v", 1),
939 FUSE_OPT_KEY("--version", 1),
943 static int ptm_process_arg(void *data
, const char *arg
, int key
,
944 struct fuse_args
*outargs
)
946 struct ptm_param
*param
= data
;
951 fprintf(stdout
, usage
, param
->prgname
);
952 return fuse_opt_add_arg(outargs
, "-ho");
955 fprintf(stdout
, "TPM emulator CUSE interface version %d.%d.%d, "
956 "Copyright (c) 2014 IBM Corp.\n",
967 int main(int argc
, char **argv
)
969 struct fuse_args args
= FUSE_ARGS_INIT(argc
, argv
);
970 struct ptm_param param
= {
980 char dev_name
[128] = "DEVNAME=";
981 const char *dev_info_argv
[] = { dev_name
};
985 if ((ret
= fuse_opt_parse(&args
, ¶m
, ptm_opts
, ptm_process_arg
))) {
986 fprintf(stderr
, "Error: Could not parse option\n");
990 if (!param
.is_help
) {
991 if (!param
.dev_name
) {
992 fprintf(stderr
, "Error: device name missing\n");
995 strncat(dev_name
, param
.dev_name
, sizeof(dev_name
) - 9);
1000 if (handle_log_options(param
.logging
) < 0 ||
1001 handle_key_options(param
.keydata
) < 0)
1005 fprintf(stderr
, "Error: Unable to setuid root. uid = %d, "
1006 "euid = %d, gid = %d\n", getuid(), geteuid(), getgid());
1011 if (!(passwd
= getpwnam(param
.runas
))) {
1012 fprintf(stderr
, "User '%s' does not exist\n",
1018 memset(&ci
, 0, sizeof(ci
));
1019 ci
.dev_major
= param
.major
;
1020 ci
.dev_minor
= param
.minor
;
1021 ci
.dev_info_argc
= 1;
1022 ci
.dev_info_argv
= dev_info_argv
;
1024 #if GLIB_MINOR_VERSION >= 32
1025 g_mutex_init(THREAD_BUSY_LOCK
);
1026 g_cond_init(THREAD_BUSY_SIGNAL
);
1027 g_mutex_init(FILE_OPS_LOCK
);
1029 g_thread_init(NULL
);
1030 THREAD_BUSY_LOCK
= g_mutex_new();
1031 THREAD_BUSY_SIGNAL
= g_cond_new();
1032 FILE_OPS_LOCK
= g_mutex_new();
1035 return cuse_lowlevel_main(args
.argc
, args
.argv
, &ci
, &ptm_clop
,