2013-01-21 13:28:14 +01:00
|
|
|
/* -*- c-basic-offset: 8 -*-
|
|
|
|
rdesktop: A Remote Desktop Protocol client.
|
|
|
|
Master/Slave remote controlling
|
2017-01-26 14:19:40 +01:00
|
|
|
Copyright 2013-2017 Henrik Andersson <hean01@cendio.se> for Cendio AB
|
2013-01-21 13:28:14 +01:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "rdesktop.h"
|
|
|
|
#include "ssl.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/un.h>
|
2017-01-26 14:19:40 +01:00
|
|
|
#include <errno.h>
|
2013-01-21 13:28:14 +01:00
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#define CTRL_LINEBUF_SIZE 1024
|
|
|
|
#define CTRL_RESULT_SIZE 32
|
2013-02-05 08:48:55 +01:00
|
|
|
#define RDESKTOP_CTRLSOCK_STORE "/.local/share/rdesktop/ctrl"
|
2013-01-21 13:28:14 +01:00
|
|
|
|
|
|
|
#define CTRL_HASH_FLAG_SEAMLESS 1
|
|
|
|
|
|
|
|
#define ERR_RESULT_OK 0x00
|
|
|
|
#define ERR_RESULT_NO_SUCH_COMMAND 0xffffffff
|
|
|
|
|
|
|
|
extern RD_BOOL g_seamless_rdp;
|
|
|
|
extern uint8 g_static_rdesktop_salt_16[];
|
2017-01-19 16:12:12 +01:00
|
|
|
extern char g_codepage[16];
|
2013-01-21 13:28:14 +01:00
|
|
|
|
|
|
|
static RD_BOOL _ctrl_is_slave;
|
|
|
|
static int ctrlsock;
|
|
|
|
static char ctrlsock_name[PATH_MAX];
|
|
|
|
static struct _ctrl_slave_t *_ctrl_slaves;
|
|
|
|
|
|
|
|
#define CMD_SEAMLESS_SPAWN "seamless.spawn"
|
|
|
|
|
|
|
|
typedef struct _ctrl_slave_t
|
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
struct _ctrl_slave_t *prev, *next;
|
|
|
|
int sock;
|
|
|
|
char linebuf[CTRL_LINEBUF_SIZE];
|
2013-01-21 13:28:14 +01:00
|
|
|
} _ctrl_slave_t;
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_slave_new(int sock);
|
|
|
|
static void _ctrl_slave_disconnect(int sock);
|
|
|
|
static void _ctrl_command_result(_ctrl_slave_t *slave, int result);
|
|
|
|
static void _ctrl_dispatch_command(_ctrl_slave_t *slave);
|
|
|
|
static RD_BOOL _ctrl_verify_unix_socket(void);
|
|
|
|
static void _ctrl_create_hash(const char *user, const char *domain, const char *host, char *hash, size_t hsize);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_slave_new(int sock)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
_ctrl_slave_t *it, *ns;
|
|
|
|
|
|
|
|
ns = (_ctrl_slave_t *)malloc(sizeof(_ctrl_slave_t));
|
|
|
|
if (!ns)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "_ctrl_slave_new(), malloc() failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(ns, 0, sizeof(_ctrl_slave_t));
|
|
|
|
ns->sock = sock;
|
|
|
|
|
|
|
|
it = _ctrl_slaves;
|
|
|
|
|
|
|
|
while (it && it->next)
|
|
|
|
it = it->next;
|
|
|
|
|
|
|
|
if (it)
|
|
|
|
{
|
|
|
|
it->next = ns;
|
|
|
|
ns->prev = it;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_ctrl_slaves = ns;
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_slave_disconnect(int sock)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
_ctrl_slave_t *it;
|
|
|
|
|
|
|
|
if (!_ctrl_slaves)
|
|
|
|
return;
|
|
|
|
|
|
|
|
it = _ctrl_slaves;
|
|
|
|
|
|
|
|
while (it->next && it->sock != sock)
|
|
|
|
it = it->next;
|
|
|
|
|
|
|
|
if (it->sock == sock)
|
|
|
|
{
|
|
|
|
shutdown(sock, SHUT_RDWR);
|
|
|
|
close(sock);
|
|
|
|
|
|
|
|
if (it == _ctrl_slaves)
|
|
|
|
{
|
|
|
|
if (it->next)
|
|
|
|
_ctrl_slaves = it->next;
|
|
|
|
else
|
|
|
|
_ctrl_slaves = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it->prev)
|
|
|
|
{
|
|
|
|
(it->prev)->next = it->next;
|
|
|
|
if (it->next)
|
|
|
|
(it->next)->prev = it->prev;
|
|
|
|
}
|
|
|
|
else if (it->next)
|
|
|
|
(it->next)->prev = NULL;
|
|
|
|
|
|
|
|
free(it);
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_command_result(_ctrl_slave_t *slave, int result)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
char buf[64] = {0};
|
|
|
|
|
|
|
|
if (result == 0)
|
|
|
|
send(slave->sock, "OK\n", 3, 0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snprintf(buf, sizeof(buf), "ERROR %x\n", result);
|
|
|
|
send(slave->sock, buf, strlen(buf), 0);
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_dispatch_command(_ctrl_slave_t *slave)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
char *cmd, *p;
|
|
|
|
unsigned int res;
|
|
|
|
|
|
|
|
cmd = utils_string_unescape(slave->linebuf);
|
|
|
|
if (!cmd)
|
|
|
|
{
|
|
|
|
_ctrl_command_result(slave, ERR_RESULT_NO_SUCH_COMMAND);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(cmd, CMD_SEAMLESS_SPAWN " ", strlen(CMD_SEAMLESS_SPAWN) + 1) == 0)
|
|
|
|
{
|
|
|
|
p = strstr(cmd, "seamlessrdpshell.exe");
|
|
|
|
if (p)
|
|
|
|
p += strlen("seamlessrdpshell.exe") + 1;
|
|
|
|
else
|
|
|
|
p = cmd + strlen(CMD_SEAMLESS_SPAWN) + 1;
|
|
|
|
|
|
|
|
res = ERR_RESULT_OK;
|
|
|
|
|
|
|
|
if (seamless_send_spawn(p) == (unsigned int)-1)
|
|
|
|
res = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
res = ERR_RESULT_NO_SUCH_COMMAND;
|
|
|
|
}
|
|
|
|
free(cmd);
|
|
|
|
|
|
|
|
_ctrl_command_result(slave, res);
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static RD_BOOL _ctrl_verify_unix_socket(void)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
int s;
|
|
|
|
struct sockaddr_un saun;
|
|
|
|
socklen_t len;
|
|
|
|
|
|
|
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
|
|
|
|
|
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "_ctrl_verify_unix_socket(), socket() failed: %s",
|
|
|
|
strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
saun.sun_family = AF_UNIX;
|
|
|
|
strncpy(saun.sun_path, ctrlsock_name, sizeof(saun.sun_path) - 1);
|
|
|
|
len = sizeof(saun.sun_family) + strlen(saun.sun_path);
|
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *)&saun, len) != 0)
|
|
|
|
{
|
|
|
|
close(s);
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
|
|
|
|
shutdown(s, SHUT_RDWR);
|
|
|
|
close(s);
|
|
|
|
return True;
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
static void _ctrl_create_hash(const char *user, const char *domain, const char *host, char *hash, size_t hsize)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
RDSSL_SHA1 sha1;
|
|
|
|
uint8 out[20], delim;
|
|
|
|
uint16 version;
|
|
|
|
uint32 flags;
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
flags = 0;
|
|
|
|
delim = '\0';
|
|
|
|
version = 0x0100;
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
if (g_seamless_rdp)
|
|
|
|
flags = CTRL_HASH_FLAG_SEAMLESS;
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
rdssl_sha1_init(&sha1);
|
|
|
|
rdssl_sha1_update(&sha1, (uint8 *)&version, sizeof(version));
|
|
|
|
rdssl_sha1_update(&sha1, &delim, 1);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
if (user)
|
|
|
|
rdssl_sha1_update(&sha1, (uint8 *)user, strlen(user));
|
|
|
|
rdssl_sha1_update(&sha1, &delim, 1);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
if (domain)
|
|
|
|
rdssl_sha1_update(&sha1, (uint8 *)domain, strlen(domain));
|
|
|
|
rdssl_sha1_update(&sha1, &delim, 1);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
if (host)
|
|
|
|
rdssl_sha1_update(&sha1, (uint8 *)host, strlen(host));
|
|
|
|
rdssl_sha1_update(&sha1, &delim, 1);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
rdssl_sha1_update(&sha1, (uint8 *)&flags, sizeof(flags));
|
|
|
|
rdssl_sha1_final(&sha1, out);
|
2013-01-21 13:28:14 +01:00
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
sec_hash_to_string(hash, hsize, out, sizeof(out));
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
int ctrl_init(const char *user, const char *domain, const char *host)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
struct stat st;
|
|
|
|
struct sockaddr_un saun;
|
|
|
|
char hash[41], path[PATH_MAX];
|
|
|
|
const char *home;
|
|
|
|
|
|
|
|
if (ctrlsock != 0 || _ctrl_is_slave)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
home = getenv("HOME");
|
|
|
|
if (home == NULL)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
_ctrl_create_hash(user, domain, host, hash, sizeof(hash));
|
|
|
|
snprintf(ctrlsock_name, sizeof(ctrlsock_name), "%s" RDESKTOP_CTRLSOCK_STORE "/%s.ctl", home, hash);
|
|
|
|
|
|
|
|
snprintf(path, sizeof(path), "%s" RDESKTOP_CTRLSOCK_STORE, home);
|
|
|
|
if (utils_mkdir_p(path, 0700) == -1)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_init(), utils_mkdir_p() failed: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(ctrlsock_name, &st) == 0)
|
|
|
|
{
|
|
|
|
if (_ctrl_verify_unix_socket() == True)
|
|
|
|
{
|
|
|
|
_ctrl_is_slave = True;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unlink(ctrlsock_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ctrlsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_init(), socket() failed: %s", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
|
|
|
saun.sun_family = AF_UNIX;
|
|
|
|
strncpy(saun.sun_path, ctrlsock_name, sizeof(saun.sun_path) - 1);
|
|
|
|
if (bind(ctrlsock, (struct sockaddr *)&saun, sizeof(struct sockaddr_un)) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_init(), bind() failed: %s", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listen(ctrlsock, 5) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_init(), listen() failed: %s", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
atexit(ctrl_cleanup);
|
|
|
|
|
|
|
|
return 0;
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
void ctrl_cleanup(void)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
if (ctrlsock)
|
|
|
|
{
|
|
|
|
close(ctrlsock);
|
|
|
|
unlink(ctrlsock_name);
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
RD_BOOL ctrl_is_slave(void)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
return _ctrl_is_slave;
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
void ctrl_add_fds(int *n, fd_set *rfds)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
_ctrl_slave_t *it;
|
|
|
|
if (ctrlsock == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
FD_SET(ctrlsock, rfds);
|
|
|
|
*n = MAX(*n, ctrlsock);
|
|
|
|
|
|
|
|
it = _ctrl_slaves;
|
|
|
|
while (it)
|
|
|
|
{
|
|
|
|
FD_SET(it->sock, rfds);
|
|
|
|
*n = MAX(*n, it->sock);
|
|
|
|
it = it->next;
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
void ctrl_check_fds(fd_set *rfds, fd_set *wfds)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
UNUSED(wfds);
|
|
|
|
int ns, res, offs;
|
|
|
|
struct sockaddr_un fsaun;
|
|
|
|
socklen_t fromlen;
|
|
|
|
_ctrl_slave_t *it;
|
|
|
|
|
|
|
|
if (ctrlsock == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&fsaun, 0, sizeof(struct sockaddr_un));
|
|
|
|
|
|
|
|
if (FD_ISSET(ctrlsock, rfds))
|
|
|
|
{
|
|
|
|
FD_CLR(ctrlsock, rfds);
|
|
|
|
fromlen = sizeof(fsaun);
|
|
|
|
ns = accept(ctrlsock, (struct sockaddr *)&fsaun, &fromlen);
|
|
|
|
if (ns < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_check_fds(), accept() failed: %s",
|
|
|
|
strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ctrl_slave_new(ns);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = _ctrl_slaves;
|
|
|
|
while (it)
|
|
|
|
{
|
|
|
|
if (FD_ISSET(it->sock, rfds))
|
|
|
|
{
|
|
|
|
offs = strlen(it->linebuf);
|
|
|
|
res = recv(it->sock, it->linebuf + offs, CTRL_LINEBUF_SIZE - offs - 1, 0);
|
|
|
|
FD_CLR(it->sock, rfds);
|
|
|
|
|
|
|
|
if (it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\0' &&
|
|
|
|
it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\n')
|
|
|
|
{
|
|
|
|
_ctrl_slave_disconnect(it->sock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res > 0)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
if ((p = strchr(it->linebuf, '\n')) == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (p)
|
|
|
|
{
|
|
|
|
if (p > it->linebuf && *(p - 1) != '\\')
|
|
|
|
break;
|
|
|
|
p = strchr(p + 1, '\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
_ctrl_dispatch_command(it);
|
|
|
|
memset(it->linebuf, 0, CTRL_LINEBUF_SIZE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_ctrl_slave_disconnect(it->sock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it = it->next;
|
|
|
|
}
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:19:45 +02:00
|
|
|
int ctrl_send_command(const char *cmd, const char *arg)
|
2013-01-21 13:28:14 +01:00
|
|
|
{
|
2024-05-31 16:19:45 +02:00
|
|
|
FILE *fp;
|
|
|
|
struct sockaddr_un saun;
|
|
|
|
int s, len, index, ret;
|
|
|
|
char data[CTRL_LINEBUF_SIZE], tmp[CTRL_LINEBUF_SIZE];
|
|
|
|
char result[CTRL_RESULT_SIZE], c, *escaped;
|
|
|
|
|
|
|
|
escaped = NULL;
|
|
|
|
|
|
|
|
if (!_ctrl_is_slave)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_send_command(), socket() failed: %s", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&saun, 0, sizeof(struct sockaddr_un));
|
|
|
|
saun.sun_family = AF_UNIX;
|
|
|
|
strncpy(saun.sun_path, ctrlsock_name, sizeof(saun.sun_path) - 1);
|
|
|
|
len = sizeof(saun.sun_family) + strlen(saun.sun_path);
|
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *)&saun, len) < 0)
|
|
|
|
{
|
|
|
|
logger(Core, Error, "ctrl_send_command(), connect() failed: %s", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(data, sizeof(data), "%s %s", cmd, arg);
|
|
|
|
ret = utils_locale_to_utf8(data, strlen(data), tmp, sizeof(tmp) - 1);
|
|
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
goto bail_out;
|
|
|
|
|
|
|
|
escaped = utils_string_escape(tmp);
|
|
|
|
if ((strlen(escaped) + 1) > sizeof(data) - 1)
|
|
|
|
goto bail_out;
|
|
|
|
|
|
|
|
send(s, escaped, strlen(escaped), 0);
|
|
|
|
send(s, "\n", 1, 0);
|
|
|
|
|
|
|
|
fp = fdopen(s, "r");
|
|
|
|
index = 0;
|
|
|
|
while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != '\n')
|
|
|
|
{
|
|
|
|
result[index] = c;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
result[index - 1] = '\0';
|
|
|
|
|
|
|
|
if (strncmp(result, "ERROR ", 6) == 0)
|
|
|
|
{
|
|
|
|
if (sscanf(result, "ERROR %d", &ret) != 1)
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bail_out:
|
|
|
|
free(escaped);
|
|
|
|
shutdown(s, SHUT_RDWR);
|
|
|
|
close(s);
|
|
|
|
|
|
|
|
return ret;
|
2013-01-21 13:28:14 +01:00
|
|
|
}
|