Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20064443
ce-server.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
36 KB
Subscribers
None
ce-server.c
View Options
/* ce-server.c - An Assuan testbed for W32CE; server code
Copyright (C) 2010 Free Software Foundation, Inc.
This file is part of Assuan.
Assuan is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 3 of
the License, or (at your option) any later version.
Assuan is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include
<config.h>
#endif
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<assert.h>
#include
<unistd.h>
#include
<sys/stat.h>
#ifdef HAVE_W32_SYSTEM
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
#endif
#include
<errno.h>
#ifdef HAVE_W32CE_SYSTEM
# ifndef FILE_ATTRIBUTE_ROMSTATICREF
# define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE
# endif
extern
BOOL
GetStdioPathW
(
int
,
wchar_t
*
,
DWORD
*
);
extern
BOOL
SetStdioPathW
(
int
,
const
wchar_t
*
);
#endif
/*!HAVE_W32CE_SYSTEM*/
#include
"../src/assuan.h"
#include
"common.h"
/* The port we are using by default. */
static
short
server_port
=
15898
;
/* Flag set to indicate a shutdown. */
static
int
shutdown_pending
;
/* An object to keep track of file descriptors. */
struct
fdinfo_s
{
struct
fdinfo_s
*
next
;
assuan_fd_t
fd
;
/* The descriptor. */
};
typedef
struct
fdinfo_s
*
fdinfo_t
;
/* The local state of a connection. */
struct
state_s
{
/* The current working directory - access using get_cwd(). */
char
*
cwd
;
/* If valid, a socket in listening state created by the dataport
command. */
assuan_fd_t
dataport_listen_fd
;
/* If valid the socket accepted for the dataport. */
assuan_fd_t
dataport_accepted_fd
;
/* The error code from a dataport accept operation. */
gpg_error_t
dataport_accept_err
;
/* A list of all unused descriptors created by dataport commands. */
fdinfo_t
dataport_fds
;
/* The descriptor set by the DATAPORT command. */
assuan_fd_t
dataport_fd
;
};
typedef
struct
state_s
*
state_t
;
/* Local prototypes. */
static
gpg_error_t
cmd_newdataport_cont
(
void
*
opaque
,
gpg_error_t
err
,
unsigned
char
*
data
,
size_t
datalen
);
/* A wrapper around read to make it work under Windows with HANDLES
and socket descriptors. Takes care of EINTR on POSIX. */
static
int
my_read
(
assuan_fd_t
fd
,
void
*
buffer
,
size_t
size
)
{
int
res
;
#ifdef HAVE_W32_SYSTEM
res
=
recv
(
HANDLE2SOCKET
(
fd
),
buffer
,
size
,
0
);
if
(
res
==
-1
)
{
switch
(
WSAGetLastError
())
{
case
WSAENOTSOCK
:
{
DWORD
nread
=
0
;
res
=
ReadFile
(
fd
,
buffer
,
size
,
&
nread
,
NULL
);
if
(
!
res
)
{
switch
(
GetLastError
())
{
case
ERROR_BROKEN_PIPE
:
gpg_err_set_errno
(
EPIPE
);
break
;
default
:
gpg_err_set_errno
(
EIO
);
}
res
=
-1
;
}
else
res
=
(
int
)
nread
;
}
break
;
case
WSAEWOULDBLOCK
:
gpg_err_set_errno
(
EAGAIN
);
break
;
case
ERROR_BROKEN_PIPE
:
gpg_err_set_errno
(
EPIPE
);
break
;
default
:
gpg_err_set_errno
(
EIO
);
break
;
}
}
return
res
;
#else
/*!HAVE_W32_SYSTEM*/
do
res
=
read
(
fd
,
buffer
,
size
);
while
(
res
==
-1
&&
errno
==
EINTR
);
return
res
;
#endif
/*!HAVE_W32_SYSTEM*/
}
/* Extended version of write(2) to guarantee that all bytes are
written. Returns 0 on success or -1 and ERRNO on failure. NOTE:
This function does not return the number of bytes written, so any
error must be treated as fatal for this connection as the state of
the receiver is unknown. This works best if blocking is allowed
(so EAGAIN cannot occur). Under Windows this function handles
socket descriptors and system handles. */
static
int
my_writen
(
assuan_fd_t
fd
,
const
char
*
buffer
,
size_t
length
)
{
while
(
length
)
{
int
nwritten
;
#ifdef HAVE_W32_SYSTEM
nwritten
=
send
(
HANDLE2SOCKET
(
fd
),
buffer
,
length
,
0
);
if
(
nwritten
==
-1
&&
WSAGetLastError
()
==
WSAENOTSOCK
)
{
DWORD
nwrite
;
nwritten
=
WriteFile
(
fd
,
buffer
,
length
,
&
nwrite
,
NULL
);
if
(
!
nwritten
)
{
switch
(
GetLastError
())
{
case
ERROR_BROKEN_PIPE
:
case
ERROR_NO_DATA
:
gpg_err_set_errno
(
EPIPE
);
break
;
default
:
gpg_err_set_errno
(
EIO
);
break
;
}
nwritten
=
-1
;
}
else
nwritten
=
(
int
)
nwrite
;
}
#else
/*!HAVE_W32_SYSTEM*/
nwritten
=
write
(
fd
,
buffer
,
length
);
#endif
/*!HAVE_W32_SYSTEM*/
if
(
nwritten
<
0
)
{
if
(
errno
==
EINTR
)
continue
;
return
-1
;
/* write error */
}
length
-=
nwritten
;
buffer
+=
nwritten
;
}
return
0
;
/* okay */
}
static
state_t
new_state
(
void
)
{
state_t
state
=
xcalloc
(
1
,
sizeof
*
state
);
state
->
dataport_listen_fd
=
ASSUAN_INVALID_FD
;
state
->
dataport_accepted_fd
=
ASSUAN_INVALID_FD
;
state
->
dataport_fd
=
ASSUAN_INVALID_FD
;
return
state
;
}
static
void
release_state
(
state_t
state
)
{
fdinfo_t
fi
,
fi2
;
if
(
!
state
)
return
;
xfree
(
state
->
cwd
);
if
(
state
->
dataport_fd
!=
ASSUAN_INVALID_FD
)
assuan_sock_close
(
state
->
dataport_fd
);
if
(
state
->
dataport_listen_fd
!=
ASSUAN_INVALID_FD
)
assuan_sock_close
(
state
->
dataport_listen_fd
);
if
(
state
->
dataport_accepted_fd
!=
ASSUAN_INVALID_FD
)
assuan_sock_close
(
state
->
dataport_accepted_fd
);
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi2
)
{
fi2
=
fi
->
next
;
if
(
fi
->
fd
!=
ASSUAN_INVALID_FD
)
assuan_sock_close
(
fi
->
fd
);
}
xfree
(
state
);
}
/* Helper to print a message while leaving a command and to
acknowledge the command. */
static
gpg_error_t
leave_cmd
(
assuan_context_t
ctx
,
gpg_error_t
err
)
{
if
(
err
)
{
const
char
*
name
=
assuan_get_command_name
(
ctx
);
if
(
!
name
)
name
=
"?"
;
if
(
gpg_err_source
(
err
)
==
GPG_ERR_SOURCE_DEFAULT
)
log_error
(
"command '%s' failed: %s
\n
"
,
name
,
gpg_strerror
(
err
));
else
log_error
(
"command '%s' failed: %s <%s>
\n
"
,
name
,
gpg_strerror
(
err
),
gpg_strsource
(
err
));
}
return
assuan_process_done
(
ctx
,
err
);
}
#ifdef HAVE_W32CE_SYSTEM
static
char
*
wchar_to_utf8
(
const
wchar_t
*
string
)
{
int
n
;
size_t
length
=
wcslen
(
string
);
char
*
result
;
n
=
WideCharToMultiByte
(
CP_UTF8
,
0
,
string
,
length
,
NULL
,
0
,
NULL
,
NULL
);
if
(
n
<
0
||
(
n
+
1
)
<=
0
)
log_fatal
(
"WideCharToMultiByte failed
\n
"
);
result
=
xmalloc
(
n
+
1
);
n
=
WideCharToMultiByte
(
CP_ACP
,
0
,
string
,
length
,
result
,
n
,
NULL
,
NULL
);
if
(
n
<
0
)
log_fatal
(
"WideCharToMultiByte failed
\n
"
);
result
[
n
]
=
0
;
return
result
;
}
static
wchar_t
*
utf8_to_wchar
(
const
char
*
string
)
{
int
n
;
size_t
length
=
strlen
(
string
);
wchar_t
*
result
;
size_t
nbytes
;
n
=
MultiByteToWideChar
(
CP_UTF8
,
0
,
string
,
length
,
NULL
,
0
);
if
(
n
<
0
||
(
n
+
1
)
<=
0
)
log_fatal
(
"MultiByteToWideChar failed
\n
"
);
nbytes
=
(
size_t
)(
n
+
1
)
*
sizeof
(
*
result
);
if
(
nbytes
/
sizeof
(
*
result
)
!=
(
n
+
1
))
log_fatal
(
"utf8_to_wchar: integer overflow
\n
"
);
result
=
xmalloc
(
nbytes
);
n
=
MultiByteToWideChar
(
CP_UTF8
,
0
,
string
,
length
,
result
,
n
);
if
(
n
<
0
)
log_fatal
(
"MultiByteToWideChar failed
\n
"
);
result
[
n
]
=
0
;
return
result
;
}
#endif
/*HAVE_W32CE_SYSTEM*/
#ifndef HAVE_W32CE_SYSTEM
static
char
*
gnu_getcwd
(
void
)
{
size_t
size
=
100
;
while
(
1
)
{
char
*
buffer
=
xmalloc
(
size
);
if
(
getcwd
(
buffer
,
size
)
==
buffer
)
return
buffer
;
xfree
(
buffer
);
if
(
errno
!=
ERANGE
)
return
0
;
size
*=
2
;
}
}
#endif
/*!HAVE_W32CE_SYSTEM*/
/* Return the current working directory. The returned string is valid
as long as STATE->cwd is not changed. */
static
const
char
*
get_cwd
(
state_t
state
)
{
if
(
!
state
->
cwd
)
{
/* No working directory yet. On WindowsCE make it the module
directory of this process. */
#ifdef HAVE_W32_SYSTEM
char
*
p
;
#endif
#ifdef HAVE_W32CE_SYSTEM
wchar_t
buf
[
MAX_PATH
+
1
];
size_t
n
;
n
=
GetModuleFileName
(
NULL
,
buf
,
MAX_PATH
);
if
(
!
n
)
state
->
cwd
=
xstrdup
(
"/"
);
else
{
buf
[
n
]
=
0
;
state
->
cwd
=
wchar_to_utf8
(
buf
);
p
=
strrchr
(
state
->
cwd
,
'\\'
);
if
(
p
)
*
p
=
0
;
}
#else
state
->
cwd
=
gnu_getcwd
();
#endif
#ifdef HAVE_W32_SYSTEM
for
(
p
=
state
->
cwd
;
*
p
;
p
++
)
if
(
*
p
==
'\\'
)
*
p
=
'/'
;
#endif
/*HAVE_W32_SYSTEM*/
}
return
state
->
cwd
;
}
static
gpg_error_t
reset_notify
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
fdinfo_t
fi
,
fi2
;
/* Close all lingering dataport connections. */
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi2
)
{
fi2
=
fi
->
next
;
if
(
fi
->
fd
!=
ASSUAN_INVALID_FD
)
assuan_sock_close
(
fi
->
fd
);
}
state
->
dataport_fds
=
NULL
;
return
0
;
}
static
gpg_error_t
input_notify
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
assuan_fd_t
fd
=
assuan_get_input_fd
(
ctx
);
fdinfo_t
fi
;
if
(
fd
!=
ASSUAN_INVALID_FD
)
{
/* The fd is now in use use - remove it from the unused list. */
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi
->
next
)
if
(
fi
->
fd
==
fd
)
fi
->
fd
=
ASSUAN_INVALID_FD
;
}
return
0
;
}
static
gpg_error_t
output_notify
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
assuan_fd_t
fd
=
assuan_get_output_fd
(
ctx
);
fdinfo_t
fi
;
if
(
fd
!=
ASSUAN_INVALID_FD
)
{
/* The fd is now in use - remove it from the unused list. */
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi
->
next
)
if
(
fi
->
fd
==
fd
)
fi
->
fd
=
ASSUAN_INVALID_FD
;
}
return
0
;
}
static
const
char
hlp_echo
[]
=
"ECHO <line>
\n
"
"
\n
"
"Print LINE as data lines.
\n
"
;
static
gpg_error_t
cmd_echo
(
assuan_context_t
ctx
,
char
*
line
)
{
gpg_error_t
err
;
err
=
assuan_send_data
(
ctx
,
line
,
strlen
(
line
));
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_cat
[]
=
"CAT [<filename>]
\n
"
"
\n
"
"Copy the content of FILENAME to the descriptor set by the OUTPUT
\n
"
"command. If no OUTPUT command has been given, send the content
\n
"
"using data lines. Without FILENAME take the content from the
\n
"
"descriptor set by the INPUT command; if a DATAPORT has been set
\n
"
"this descriptor is used for I/O and the INOPUT/OUTPUT descriptors
\n
"
"are not touched."
;
static
gpg_error_t
cmd_cat
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
=
0
;
assuan_fd_t
fd_in
=
ASSUAN_INVALID_FD
;
assuan_fd_t
fd_out
=
ASSUAN_INVALID_FD
;
FILE
*
fp_in
=
NULL
;
char
buf
[
256
];
size_t
nread
;
int
use_dataport
=
0
;
if
(
*
line
)
{
fp_in
=
fopen
(
line
,
"rb"
);
if
(
!
fp_in
)
err
=
gpg_error_from_syserror
();
else
fd_out
=
assuan_get_output_fd
(
ctx
);
}
else
if
(
state
->
dataport_fd
!=
ASSUAN_INVALID_FD
)
{
use_dataport
=
1
;
fd_in
=
state
->
dataport_fd
;
fd_out
=
state
->
dataport_fd
;
}
else
if
((
fd_in
=
assuan_get_input_fd
(
ctx
))
!=
ASSUAN_INVALID_FD
)
{
/* This FD is actually a socket descriptor. We can't fdopen it
because under Windows we ust use recv(2) instead of read(2).
Note that on POSIX systems there is no difference between
libc file descriptors and socket descriptors. */
fd_out
=
assuan_get_output_fd
(
ctx
);
}
else
err
=
gpg_error
(
GPG_ERR_ASS_NO_INPUT
);
if
(
err
)
goto
leave
;
do
{
if
(
fp_in
)
{
nread
=
fread
(
buf
,
1
,
sizeof
buf
,
fp_in
);
if
(
nread
<
sizeof
buf
)
{
if
(
ferror
(
fp_in
))
err
=
gpg_error_from_syserror
();
else
if
(
feof
(
fp_in
))
err
=
gpg_error
(
GPG_ERR_EOF
);
}
}
else
{
int
n
;
nread
=
0
;
n
=
my_read
(
fd_in
,
buf
,
sizeof
buf
);
if
(
n
<
0
)
err
=
gpg_error_from_syserror
();
else
if
(
!
n
)
err
=
gpg_error
(
GPG_ERR_EOF
);
else
nread
=
n
;
}
if
(
fd_out
!=
ASSUAN_INVALID_FD
)
{
if
(
nread
&&
my_writen
(
fd_out
,
buf
,
nread
))
err
=
gpg_error_from_syserror
();
}
else
if
(
nread
)
err
=
assuan_send_data
(
ctx
,
buf
,
nread
);
}
while
(
!
err
);
if
(
gpg_err_code
(
err
)
==
GPG_ERR_EOF
)
err
=
0
;
leave
:
if
(
fp_in
)
fclose
(
fp_in
);
if
(
use_dataport
)
{
if
(
state
->
dataport_fd
!=
ASSUAN_INVALID_FD
)
{
assuan_sock_close
(
state
->
dataport_fd
);
state
->
dataport_fd
=
ASSUAN_INVALID_FD
;
}
}
else
{
assuan_close_input_fd
(
ctx
);
assuan_close_output_fd
(
ctx
);
}
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_pwd
[]
=
"PWD
\n
"
"
\n
"
"Print the curent working directory of this session.
\n
"
;
static
gpg_error_t
cmd_pwd
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
;
const
char
*
string
;
string
=
get_cwd
(
state
);
err
=
assuan_send_data
(
ctx
,
string
,
strlen
(
string
));
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_cd
[]
=
"CD [dir]
\n
"
"
\n
"
"Change the curretn directory of the session.
\n
"
;
static
gpg_error_t
cmd_cd
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
=
0
;
char
*
newdir
,
*
p
;
for
(
p
=
line
;
*
p
;
p
++
)
if
(
*
p
==
'\\'
)
*
p
=
'/'
;
if
(
!*
line
)
{
xfree
(
state
->
cwd
);
state
->
cwd
=
NULL
;
get_cwd
(
state
);
}
else
{
if
(
*
line
==
'/'
)
newdir
=
xstrdup
(
line
);
else
newdir
=
xstrconcat
(
get_cwd
(
state
),
"/"
,
line
,
NULL
);
while
(
strlen
(
newdir
)
>
1
&&
line
[
strlen
(
newdir
)
-1
]
==
'/'
)
line
[
strlen
(
newdir
)
-1
]
=
0
;
xfree
(
state
->
cwd
);
state
->
cwd
=
newdir
;
}
return
leave_cmd
(
ctx
,
err
);
}
#ifdef HAVE_W32CE_SYSTEM
static
const
char
hlp_ls
[]
=
"LS [<pattern>]
\n
"
"
\n
"
"List the files described by PATTERN.
\n
"
;
static
gpg_error_t
cmd_ls
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
;
WIN32_FIND_DATA
fi
;
char
buf
[
500
];
HANDLE
hd
;
char
*
p
,
*
fname
;
wchar_t
*
wfname
;
if
(
!*
line
)
fname
=
xstrconcat
(
get_cwd
(
state
),
"/*"
,
NULL
);
else
if
(
*
line
==
'/'
||
*
line
==
'\\'
)
fname
=
xstrdup
(
line
);
else
fname
=
xstrconcat
(
get_cwd
(
state
),
"/"
,
line
,
NULL
);
for
(
p
=
fname
;
*
p
;
p
++
)
if
(
*
p
==
'/'
)
*
p
=
'\\'
;
assuan_write_status
(
ctx
,
"PATTERN"
,
fname
);
wfname
=
utf8_to_wchar
(
fname
);
xfree
(
fname
);
hd
=
FindFirstFile
(
wfname
,
&
fi
);
free
(
wfname
);
if
(
hd
==
INVALID_HANDLE_VALUE
)
{
log_info
(
"FindFirstFile returned %d
\n
"
,
GetLastError
());
err
=
gpg_error_from_syserror
();
/* Works for W32CE. */
goto
leave
;
}
do
{
DWORD
attr
=
fi
.
dwFileAttributes
;
fname
=
wchar_to_utf8
(
fi
.
cFileName
);
snprintf
(
buf
,
sizeof
buf
,
"%c%c%c%c%c%c%c%c%c%c%c%c%c %7lu%c %s
\n
"
,
(
attr
&
FILE_ATTRIBUTE_DIRECTORY
)
?
((
attr
&
FILE_ATTRIBUTE_DEVICE
)
?
'c'
:
'd'
)
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_READONLY
)
?
'r'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_HIDDEN
)
?
'h'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_SYSTEM
)
?
's'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_ARCHIVE
)
?
'a'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_COMPRESSED
)
?
'c'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_ENCRYPTED
)
?
'e'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_INROM
)
?
'R'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_REPARSE_POINT
)
?
'P'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_ROMMODULE
)
?
'M'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_ROMSTATICREF
)
?
'R'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_SPARSE_FILE
)
?
'S'
:
'-'
,
(
attr
&
FILE_ATTRIBUTE_TEMPORARY
)
?
't'
:
'-'
,
(
unsigned
long
)
fi
.
nFileSizeLow
,
fi
.
nFileSizeHigh
?
'X'
:
' '
,
fname
);
free
(
fname
);
err
=
assuan_send_data
(
ctx
,
buf
,
strlen
(
buf
));
if
(
!
err
)
err
=
assuan_send_data
(
ctx
,
NULL
,
0
);
}
while
(
!
err
&&
FindNextFile
(
hd
,
&
fi
));
if
(
err
)
;
else
if
(
GetLastError
()
==
ERROR_NO_MORE_FILES
)
err
=
0
;
else
{
log_info
(
"FindNextFile returned %d
\n
"
,
GetLastError
());
err
=
gpg_error_from_syserror
();
}
FindClose
(
hd
);
leave
:
return
leave_cmd
(
ctx
,
err
);
}
#endif
/*HAVE_W32CE_SYSTEM*/
#ifdef HAVE_W32CE_SYSTEM
static
const
char
hlp_run
[]
=
"RUN <filename> [<args>]
\n
"
"
\n
"
"Run the program in FILENAME with the arguments ARGS.
\n
"
"This creates a new process and waits for it to finish.
\n
"
"FIXME: The process' stdin is connected to the file set by the
\n
"
"INPUT command; stdout and stderr to the one set by OUTPUT.
\n
"
;
static
gpg_error_t
cmd_run
(
assuan_context_t
ctx
,
char
*
line
)
{
/* state_t state = assuan_get_pointer (ctx); */
gpg_error_t
err
;
BOOL
w32ret
;
PROCESS_INFORMATION
pi
=
{
NULL
,
0
,
0
,
0
};
char
*
p
;
wchar_t
*
pgmname
=
NULL
;
wchar_t
*
cmdline
=
NULL
;
int
code
;
DWORD
exc
;
int
idx
;
struct
{
HANDLE
hd
[
2
];
int
oldname_valid
;
wchar_t
oldname
[
MAX_PATH
];
}
pipes
[
3
];
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
pipes
[
idx
].
hd
[
0
]
=
pipes
[
idx
].
hd
[
1
]
=
INVALID_HANDLE_VALUE
;
pipes
[
idx
].
oldname_valid
=
0
;
}
p
=
strchr
(
line
,
' '
);
if
(
p
)
{
*
p
=
0
;
pgmname
=
utf8_to_wchar
(
line
);
for
(
p
++
;
*
p
&&
*
p
==
' '
;
p
++
)
;
cmdline
=
utf8_to_wchar
(
p
);
}
else
pgmname
=
utf8_to_wchar
(
line
);
{
char
*
tmp1
=
wchar_to_utf8
(
pgmname
);
char
*
tmp2
=
wchar_to_utf8
(
cmdline
);
log_info
(
"CreateProcess, path=`%s' cmdline=`%s'
\n
"
,
tmp1
,
tmp2
);
xfree
(
tmp2
);
xfree
(
tmp1
);
}
/* Redirect the standard handles. */
/* Create pipes. */
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
if
(
!
_assuan_w32ce_create_pipe
(
&
pipes
[
idx
].
hd
[
0
],
&
pipes
[
idx
].
hd
[
1
],
NULL
,
0
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"CreatePipe failed: %d
\n
"
,
GetLastError
());
pipes
[
idx
].
hd
[
0
]
=
pipes
[
idx
].
hd
[
1
]
=
INVALID_HANDLE_VALUE
;
goto
leave
;
}
}
/* Save currently assigned devices. */
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
DWORD
dwlen
=
MAX_PATH
;
if
(
!
GetStdioPathW
(
idx
,
pipes
[
idx
].
oldname
,
&
dwlen
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"GetStdioPath failed: %d
\n
"
,
GetLastError
());
goto
leave
;
}
pipes
[
idx
].
oldname_valid
=
1
;
}
/* Connect the pipes. */
{
if
(
!
SetStdioPathW
(
1
,
L
"
\\
mystdout.log"
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"SetStdioPathW(%d) failed: %d
\n
"
,
idx
,
GetLastError
());
goto
leave
;
}
if
(
!
SetStdioPathW
(
2
,
L
"
\\
mystderr.log"
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"SetStdioPathW(%d) failed: %d
\n
"
,
idx
,
GetLastError
());
goto
leave
;
}
}
/* Create the process, restore the devices and check the error. */
w32ret
=
CreateProcess
(
pgmname
,
/* Program to start. */
cmdline
,
/* Command line arguments. */
NULL
,
/* Process security. Not used. */
NULL
,
/* Thread security. Not used. */
FALSE
,
/* Inherit handles. Not used. */
CREATE_SUSPENDED
,
/* Creation flags. */
NULL
,
/* Environment. Not used. */
NULL
,
/* Use current dir. Not used. */
NULL
,
/* Startup information. Not used. */
&
pi
/* Returns process information. */
);
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
if
(
pipes
[
idx
].
oldname_valid
)
{
if
(
!
SetStdioPathW
(
idx
,
pipes
[
idx
].
oldname
))
log_error
(
"SetStdioPath(%d) failed during restore: %d
\n
"
,
idx
,
GetLastError
());
else
pipes
[
idx
].
oldname_valid
=
0
;
}
}
if
(
!
w32ret
)
{
/* Error checking after restore so that the messages are visible. */
log_error
(
"CreateProcess failed: %d
\n
"
,
GetLastError
());
err
=
gpg_error_from_syserror
();
goto
leave
;
}
log_info
(
"CreateProcess ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d
\n
"
,
pi
.
hProcess
,
pi
.
hThread
,
(
int
)
pi
.
dwProcessId
,
(
int
)
pi
.
dwThreadId
);
ResumeThread
(
pi
.
hThread
);
CloseHandle
(
pi
.
hThread
);
code
=
WaitForSingleObject
(
pi
.
hProcess
,
INFINITE
);
switch
(
code
)
{
case
WAIT_FAILED
:
err
=
gpg_error_from_syserror
();;
log_error
(
"waiting for process %d to terminate failed: %d
\n
"
,
(
int
)
pi
.
dwProcessId
,
GetLastError
());
break
;
case
WAIT_OBJECT_0
:
if
(
!
GetExitCodeProcess
(
pi
.
hProcess
,
&
exc
))
{
err
=
gpg_error_from_syserror
();;
log_error
(
"error getting exit code of process %d: %s
\n
"
,
(
int
)
pi
.
dwProcessId
,
GetLastError
()
);
}
else
if
(
exc
)
{
log_info
(
"error running process: exit status %d
\n
"
,
(
int
)
exc
);
err
=
gpg_error
(
GPG_ERR_GENERAL
);
}
else
{
err
=
0
;
}
break
;
default
:
err
=
gpg_error_from_syserror
();;
log_error
(
"WaitForSingleObject returned unexpected "
"code %d for pid %d
\n
"
,
code
,
(
int
)
pi
.
dwProcessId
);
break
;
}
CloseHandle
(
pi
.
hProcess
);
leave
:
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
if
(
pipes
[
idx
].
oldname_valid
)
{
if
(
!
SetStdioPathW
(
idx
,
pipes
[
idx
].
oldname
))
log_error
(
"SetStdioPath(%d) failed during restore: %d
\n
"
,
idx
,
GetLastError
());
else
pipes
[
idx
].
oldname_valid
=
0
;
}
}
for
(
idx
=
0
;
idx
<
3
;
idx
++
)
{
if
(
pipes
[
idx
].
hd
[
0
]
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
pipes
[
idx
].
hd
[
0
]);
if
(
pipes
[
idx
].
hd
[
1
]
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
pipes
[
idx
].
hd
[
1
]);
}
xfree
(
cmdline
);
xfree
(
pgmname
);
return
leave_cmd
(
ctx
,
err
);
}
#endif
/*HAVE_W32CE_SYSTEM*/
static
const
char
hlp_newdataport
[]
=
"NEWDATAPORT
\n
"
"
\n
"
"Create a new dataport. The server creates a listening socket and
\n
"
"issues the inquiry:
\n
"
" INQUIRE CONNECT-TO <port>
\n
"
"The client is expected to connect to PORT of the server and confirm
\n
"
"this by sending just an
\"
END
\"
. In turn the server sends:
\n
"
" S FDINFO <n>
\n
"
"With N being the local descriptor for the accepted connection. This
\n
"
"descriptor may now be used with INPUT or OUTPUT commands."
;
struct
cmd_dataport_locals
{
assuan_context_t
ctx
;
int
passthru
;
int
port
;
};
static
gpg_error_t
cmd_newdataport
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
=
0
;
struct
sockaddr_in
addr
;
socklen_t
addrlen
;
struct
cmd_dataport_locals
*
cont
;
char
inqline
[
100
];
cont
=
xmalloc
(
sizeof
*
cont
);
cont
->
ctx
=
ctx
;
cont
->
passthru
=
0
;
cont
->
port
=
0
;
if
(
state
->
dataport_listen_fd
!=
ASSUAN_INVALID_FD
)
{
log_error
(
"Oops, still listening on a dataport socket
\n
"
);
state
->
dataport_listen_fd
=
ASSUAN_INVALID_FD
;
}
if
(
state
->
dataport_accepted_fd
!=
ASSUAN_INVALID_FD
)
{
log_error
(
"Oops, still holding an accepted dataport socket
\n
"
);
state
->
dataport_accepted_fd
=
ASSUAN_INVALID_FD
;
}
state
->
dataport_accept_err
=
0
;
state
->
dataport_listen_fd
=
assuan_sock_new
(
PF_INET
,
SOCK_STREAM
,
0
);
if
(
state
->
dataport_listen_fd
==
ASSUAN_INVALID_FD
)
{
err
=
gpg_error_from_syserror
();
log_error
(
"socket() failed: %s
\n
"
,
strerror
(
errno
));
goto
leave
;
}
addr
.
sin_family
=
AF_INET
;
addr
.
sin_port
=
0
;
addr
.
sin_addr
.
s_addr
=
htonl
(
INADDR_ANY
);
if
(
assuan_sock_bind
(
state
->
dataport_listen_fd
,
(
struct
sockaddr
*
)
&
addr
,
sizeof
addr
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"listen() failed: %s
\n
"
,
strerror
(
errno
));
goto
leave
;
}
if
(
listen
(
HANDLE2SOCKET
(
state
->
dataport_listen_fd
),
1
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"listen() failed: %s
\n
"
,
strerror
(
errno
));
goto
leave
;
}
addrlen
=
sizeof
addr
;
if
(
getsockname
(
HANDLE2SOCKET
(
state
->
dataport_listen_fd
),
(
struct
sockaddr
*
)
&
addr
,
&
addrlen
))
{
err
=
gpg_error_from_syserror
();
log_error
(
"getsockname() failed: %s
\n
"
,
strerror
(
errno
));
goto
leave
;
}
cont
->
port
=
ntohs
(
addr
.
sin_port
);
if
(
verbose
)
log_info
(
"server now also listening on port %d
\n
"
,
cont
->
port
);
snprintf
(
inqline
,
sizeof
inqline
,
"CONNECT-TO %d"
,
cont
->
port
);
err
=
assuan_inquire_ext
(
ctx
,
inqline
,
0
,
cmd_newdataport_cont
,
cont
);
if
(
!
err
)
return
0
;
/* Transfer to continuation. */
leave
:
cont
->
passthru
=
1
;
return
cmd_newdataport_cont
(
cont
,
err
,
NULL
,
0
);
}
/* Continuation used by cmd_newdataport. */
static
gpg_error_t
cmd_newdataport_cont
(
void
*
opaque
,
gpg_error_t
err
,
unsigned
char
*
data
,
size_t
datalen
)
{
struct
cmd_dataport_locals
*
cont
=
opaque
;
assuan_context_t
ctx
=
cont
->
ctx
;
state_t
state
=
assuan_get_pointer
(
ctx
);
char
numbuf
[
35
];
fdinfo_t
fi
;
if
(
cont
->
passthru
||
err
)
goto
leave
;
err
=
state
->
dataport_accept_err
;
if
(
err
)
goto
leave
;
if
(
state
->
dataport_listen_fd
!=
ASSUAN_INVALID_FD
||
state
->
dataport_accepted_fd
==
ASSUAN_INVALID_FD
)
{
err
=
gpg_error
(
GPG_ERR_MISSING_ACTION
);
goto
leave
;
}
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi
->
next
)
if
(
fi
->
fd
==
ASSUAN_INVALID_FD
)
break
;
if
(
!
fi
)
{
fi
=
xcalloc
(
1
,
sizeof
*
fi
);
fi
->
next
=
state
->
dataport_fds
;
state
->
dataport_fds
=
fi
;
}
fi
->
fd
=
state
->
dataport_accepted_fd
;
state
->
dataport_accepted_fd
=
ASSUAN_INVALID_FD
;
/* Note that under Windows the FD is the socket descriptor. Socket
descriptors are neither handles nor libc file descriptors. */
snprintf
(
numbuf
,
sizeof
numbuf
,
"%d"
,
HANDLE2SOCKET
(
fi
->
fd
));
err
=
assuan_write_status
(
ctx
,
"FDINFO"
,
numbuf
);
leave
:
if
(
state
->
dataport_listen_fd
!=
ASSUAN_INVALID_FD
)
{
assuan_sock_close
(
state
->
dataport_listen_fd
);
state
->
dataport_listen_fd
=
ASSUAN_INVALID_FD
;
}
if
(
state
->
dataport_accepted_fd
!=
ASSUAN_INVALID_FD
)
{
assuan_sock_close
(
state
->
dataport_accepted_fd
);
state
->
dataport_accepted_fd
=
ASSUAN_INVALID_FD
;
}
xfree
(
cont
);
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_dataport
[]
=
"DATAPORT FD[=<n>]
\n
"
"
\n
"
"Set the file descriptor to read and write data via port.
\n
"
"This is similar to the
\"
INPUT
\"
and
\"
OUTPUT
\"
commands
\n
"
"but useful for socketpairs."
;
static
gpg_error_t
cmd_dataport
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
;
assuan_fd_t
fd
;
if
(
state
->
dataport_fd
!=
ASSUAN_INVALID_FD
)
{
assuan_sock_close
(
state
->
dataport_fd
);
state
->
dataport_fd
=
ASSUAN_INVALID_FD
;
}
err
=
assuan_command_parse_fd
(
ctx
,
line
,
&
fd
);
if
(
!
err
&&
fd
!=
ASSUAN_INVALID_FD
)
{
fdinfo_t
fi
;
state
->
dataport_fd
=
fd
;
/* The fd is now in use use - remove it from the unused list. */
for
(
fi
=
state
->
dataport_fds
;
fi
;
fi
=
fi
->
next
)
if
(
fi
->
fd
==
fd
)
fi
->
fd
=
ASSUAN_INVALID_FD
;
}
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_getinfo
[]
=
"GETINFO <what>
\n
"
"
\n
"
"Multipurpose function to return a variety of information.
\n
"
"Supported values for WHAT are:
\n
"
"
\n
"
" version - Return the version of the program.
\n
"
" pid - Return the process id of the server.
\n
"
" dataports - Return a list of usused dataports."
;
static
gpg_error_t
cmd_getinfo
(
assuan_context_t
ctx
,
char
*
line
)
{
state_t
state
=
assuan_get_pointer
(
ctx
);
gpg_error_t
err
=
0
;
char
numbuf
[
50
];
if
(
!
strcmp
(
line
,
"version"
))
{
const
char
*
s
=
VERSION
;
err
=
assuan_send_data
(
ctx
,
s
,
strlen
(
s
));
}
else
if
(
!
strcmp
(
line
,
"pid"
))
{
snprintf
(
numbuf
,
sizeof
numbuf
,
"%lu"
,
(
unsigned
long
)
getpid
());
err
=
assuan_send_data
(
ctx
,
numbuf
,
strlen
(
numbuf
));
}
else
if
(
!
strcmp
(
line
,
"dataports"
))
{
fdinfo_t
fi
;
int
any
=
0
;
for
(
fi
=
state
->
dataport_fds
;
!
err
&&
fi
;
fi
=
fi
->
next
)
{
if
(
fi
->
fd
!=
ASSUAN_INVALID_FD
)
{
snprintf
(
numbuf
,
sizeof
numbuf
,
"%s%d"
,
any
?
" "
:
""
,
HANDLE2SOCKET
(
fi
->
fd
));
any
=
1
;
err
=
assuan_send_data
(
ctx
,
numbuf
,
strlen
(
numbuf
));
}
}
}
else
err
=
gpg_error
(
GPG_ERR_ASS_PARAMETER
);
return
leave_cmd
(
ctx
,
err
);
}
static
const
char
hlp_shutdown
[]
=
"SHUTDOWN
\n
"
"
\n
"
"Shutdown the server process
\n
"
;
static
gpg_error_t
cmd_shutdown
(
assuan_context_t
ctx
,
char
*
line
)
{
(
void
)
line
;
shutdown_pending
=
1
;
return
leave_cmd
(
ctx
,
0
);;
}
static
gpg_error_t
register_commands
(
assuan_context_t
ctx
)
{
static
struct
{
const
char
*
name
;
gpg_error_t
(
*
handler
)
(
assuan_context_t
,
char
*
line
);
const
char
*
const
help
;
}
table
[]
=
{
#ifdef HAVE_W32CE_SYSTEM
{
"LS"
,
cmd_ls
,
hlp_ls
},
{
"RUN"
,
cmd_run
,
hlp_run
},
#endif
{
"PWD"
,
cmd_pwd
,
hlp_pwd
},
{
"CD"
,
cmd_cd
,
hlp_cd
},
{
"ECHO"
,
cmd_echo
,
hlp_echo
},
{
"CAT"
,
cmd_cat
,
hlp_cat
},
{
"NEWDATAPORT"
,
cmd_newdataport
,
hlp_newdataport
},
{
"DATAPORT"
,
cmd_dataport
,
hlp_dataport
},
{
"INPUT"
,
NULL
},
{
"OUTPUT"
,
NULL
},
{
"GETINFO"
,
cmd_getinfo
,
hlp_getinfo
},
{
"SHUTDOWN"
,
cmd_shutdown
,
hlp_shutdown
},
{
NULL
,
NULL
}
};
int
i
;
gpg_error_t
rc
;
for
(
i
=
0
;
table
[
i
].
name
;
i
++
)
{
rc
=
assuan_register_command
(
ctx
,
table
[
i
].
name
,
table
[
i
].
handler
,
table
[
i
].
help
);
if
(
rc
)
return
rc
;
}
return
0
;
}
static
assuan_fd_t
get_connection_fd
(
assuan_context_t
ctx
)
{
assuan_fd_t
fds
[
5
];
if
(
assuan_get_active_fds
(
ctx
,
0
,
fds
,
DIM
(
fds
))
<
1
)
log_fatal
(
"assuan_get_active_fds failed
\n
"
);
if
(
fds
[
0
]
==
ASSUAN_INVALID_FD
)
log_fatal
(
"assuan_get_active_fds returned invalid conenction fd
\n
"
);
return
fds
[
0
];
}
/* Startup the server. */
static
void
server
(
void
)
{
gpg_error_t
err
;
assuan_fd_t
server_fd
;
assuan_sock_nonce_t
server_nonce
;
int
one
=
1
;
struct
sockaddr_in
name
;
assuan_context_t
ctx
;
state_t
state
=
NULL
;
err
=
assuan_new
(
&
ctx
);
if
(
err
)
log_fatal
(
"assuan_new failed: %s
\n
"
,
gpg_strerror
(
err
));
server_fd
=
assuan_sock_new
(
PF_INET
,
SOCK_STREAM
,
0
);
if
(
server_fd
==
ASSUAN_INVALID_FD
)
log_fatal
(
"socket() failed: %s
\n
"
,
strerror
(
errno
));
if
(
setsockopt
(
HANDLE2SOCKET
(
server_fd
),
SOL_SOCKET
,
SO_REUSEADDR
,
(
void
*
)
&
one
,
sizeof
one
))
log_error
(
"setsockopt(SO_REUSEADDR) failed: %s
\n
"
,
strerror
(
errno
));
name
.
sin_family
=
AF_INET
;
name
.
sin_port
=
htons
(
server_port
);
name
.
sin_addr
.
s_addr
=
htonl
(
INADDR_ANY
);
if
(
assuan_sock_bind
(
server_fd
,
(
struct
sockaddr
*
)
&
name
,
sizeof
name
))
log_fatal
(
"bind() failed: %s
\n
"
,
strerror
(
errno
));
if
(
assuan_sock_get_nonce
((
struct
sockaddr
*
)
&
name
,
sizeof
name
,
&
server_nonce
))
log_fatal
(
"assuan_sock_get_nonce failed: %s
\n
"
,
strerror
(
errno
));
/* Register the nonce with the context so that assuan_accept knows
about it. We can't do that directly in assuan_sock_bind because
we want these socket wrappers to be context neutral and drop in
replacement for the standard socket functions. */
assuan_set_sock_nonce
(
ctx
,
&
server_nonce
);
if
(
listen
(
HANDLE2SOCKET
(
server_fd
),
5
))
log_fatal
(
"listen() failed: %s
\n
"
,
strerror
(
errno
));
log_info
(
"server listening on port %hd
\n
"
,
server_port
);
err
=
assuan_init_socket_server
(
ctx
,
server_fd
,
0
);
if
(
err
)
log_fatal
(
"assuan_init_socket_server failed: %s
\n
"
,
gpg_strerror
(
err
));
err
=
register_commands
(
ctx
);
if
(
err
)
log_fatal
(
"register_commands failed: %s
\n
"
,
gpg_strerror
(
err
));
if
(
debug
)
assuan_set_log_stream
(
ctx
,
stderr
);
assuan_register_reset_notify
(
ctx
,
reset_notify
);
assuan_register_input_notify
(
ctx
,
input_notify
);
assuan_register_output_notify
(
ctx
,
output_notify
);
state
=
new_state
();
assuan_set_pointer
(
ctx
,
state
);
while
(
!
shutdown_pending
)
{
int
done
;
err
=
assuan_accept
(
ctx
);
if
(
err
)
{
if
(
gpg_err_code
(
err
)
==
GPG_ERR_EOF
||
err
==
-1
)
log_error
(
"assuan_accept failed: %s
\n
"
,
gpg_strerror
(
err
));
break
;
}
log_info
(
"client connected. Client's pid is %ld
\n
"
,
(
long
)
assuan_get_pid
(
ctx
));
do
{
/* We need to use select here so that we can accept
supplemental connections from the client as requested by
the DATAPORT command. */
fd_set
rfds
;
int
connfd
,
datafd
,
max_fd
;
connfd
=
HANDLE2SOCKET
(
get_connection_fd
(
ctx
));
FD_ZERO
(
&
rfds
);
FD_SET
(
connfd
,
&
rfds
);
max_fd
=
connfd
;
if
(
state
->
dataport_listen_fd
!=
ASSUAN_INVALID_FD
)
{
datafd
=
HANDLE2SOCKET
(
state
->
dataport_listen_fd
);
FD_SET
(
datafd
,
&
rfds
);
if
(
datafd
>
max_fd
)
max_fd
=
datafd
;
}
else
datafd
=
-1
;
if
(
select
(
max_fd
+
1
,
&
rfds
,
NULL
,
NULL
,
NULL
)
>
0
)
{
if
(
datafd
!=
-1
&&
FD_ISSET
(
datafd
,
&
rfds
))
{
struct
sockaddr_in
clnt_addr
;
socklen_t
len
=
sizeof
clnt_addr
;
int
fd
;
fd
=
accept
(
datafd
,
(
struct
sockaddr
*
)
&
clnt_addr
,
&
len
);
if
(
fd
==
-1
)
{
err
=
gpg_err_code_from_syserror
();
assuan_sock_close
(
state
->
dataport_listen_fd
);
state
->
dataport_listen_fd
=
ASSUAN_INVALID_FD
;
log_error
(
"accepting on dataport failed: %s
\n
"
,
gpg_strerror
(
err
));
state
->
dataport_accept_err
=
err
;
err
=
0
;
}
else
{
/* No more need for the listening socket. */
assuan_sock_close
(
state
->
dataport_listen_fd
);
state
->
dataport_listen_fd
=
ASSUAN_INVALID_FD
;
/* Record the accepted fd. */
state
->
dataport_accept_err
=
0
;
state
->
dataport_accepted_fd
=
SOCKET2HANDLE
(
fd
);
}
}
if
(
FD_ISSET
(
connfd
,
&
rfds
))
{
err
=
assuan_process_next
(
ctx
,
&
done
);
}
}
}
while
(
!
err
&&
!
done
&&
!
shutdown_pending
);
if
(
err
)
log_error
(
"assuan_process failed: %s
\n
"
,
gpg_strerror
(
err
));
}
assuan_sock_close
(
server_fd
);
assuan_release
(
ctx
);
release_state
(
state
);
}
/*
M A I N
*/
int
main
(
int
argc
,
char
**
argv
)
{
gpg_error_t
err
;
int
last_argc
=
-1
;
if
(
argc
)
{
log_set_prefix
(
*
argv
);
argc
--
;
argv
++
;
}
while
(
argc
&&
last_argc
!=
argc
)
{
last_argc
=
argc
;
if
(
!
strcmp
(
*
argv
,
"--help"
))
{
printf
(
"usage: %s [options]
\n
"
"
\n
"
"Options:
\n
"
" --verbose Show what is going on
\n
"
,
log_get_prefix
());
exit
(
0
);
}
if
(
!
strcmp
(
*
argv
,
"--verbose"
))
{
verbose
=
1
;
argc
--
;
argv
++
;
}
else
if
(
!
strcmp
(
*
argv
,
"--debug"
))
{
verbose
=
debug
=
1
;
argc
--
;
argv
++
;
}
}
assuan_set_assuan_log_prefix
(
log_prefix
);
if
(
debug
)
assuan_set_assuan_log_stream
(
stderr
);
err
=
assuan_sock_init
();
if
(
err
)
log_fatal
(
"assuan_sock_init failed: %s
\n
"
,
gpg_strerror
(
err
));
log_info
(
"server starting...
\n
"
);
server
();
log_info
(
"server finished
\n
"
);
assuan_sock_deinit
();
return
errorcount
?
1
:
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Feb 23, 7:13 PM (21 m, 44 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
13/76/99989bd0aae49e3a5758401b800f
Attached To
rA Assuan
Event Timeline
Log In to Comment