xref: /openbmc/obmc-console/console-client.c (revision 1e04f449b7f00a7b426615533a44519149d2ea38)
19326d779SJeremy Kerr /**
29326d779SJeremy Kerr  * Copyright © 2016 IBM Corporation
39326d779SJeremy Kerr  *
49326d779SJeremy Kerr  * Licensed under the Apache License, Version 2.0 (the "License");
59326d779SJeremy Kerr  * you may not use this file except in compliance with the License.
69326d779SJeremy Kerr  * You may obtain a copy of the License at
79326d779SJeremy Kerr  *
89326d779SJeremy Kerr  *     http://www.apache.org/licenses/LICENSE-2.0
99326d779SJeremy Kerr  *
109326d779SJeremy Kerr  * Unless required by applicable law or agreed to in writing, software
119326d779SJeremy Kerr  * distributed under the License is distributed on an "AS IS" BASIS,
129326d779SJeremy Kerr  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139326d779SJeremy Kerr  * See the License for the specific language governing permissions and
149326d779SJeremy Kerr  * limitations under the License.
159326d779SJeremy Kerr  */
162bd05186SJeremy Kerr 
172bd05186SJeremy Kerr #include <err.h>
185e7c0786SAndrew Jeffery #include <errno.h>
1915691c8eSWilliam A. Kennington III #include <getopt.h>
202bd05186SJeremy Kerr #include <stdbool.h>
212bd05186SJeremy Kerr #include <stdint.h>
222bd05186SJeremy Kerr #include <stdio.h>
232bd05186SJeremy Kerr #include <stdlib.h>
242bd05186SJeremy Kerr #include <string.h>
252bd05186SJeremy Kerr #include <termios.h>
262bd05186SJeremy Kerr #include <unistd.h>
272bd05186SJeremy Kerr 
282bd05186SJeremy Kerr #include <sys/socket.h>
292bd05186SJeremy Kerr #include <sys/un.h>
302bd05186SJeremy Kerr 
312bd05186SJeremy Kerr #include "console-server.h"
32*1e04f449SAlexander Hansen #include "config.h"
332bd05186SJeremy Kerr 
348a154357SWilliam A. Kennington III #define EXIT_ESCAPE 2
358a154357SWilliam A. Kennington III 
362bd05186SJeremy Kerr enum process_rc {
372bd05186SJeremy Kerr 	PROCESS_OK = 0,
382bd05186SJeremy Kerr 	PROCESS_ERR,
392bd05186SJeremy Kerr 	PROCESS_EXIT,
408a154357SWilliam A. Kennington III 	PROCESS_ESC,
412bd05186SJeremy Kerr };
422bd05186SJeremy Kerr 
43ff569830SWilliam A. Kennington III enum esc_type {
44ff569830SWilliam A. Kennington III 	ESC_TYPE_SSH,
4515691c8eSWilliam A. Kennington III 	ESC_TYPE_STR,
46ff569830SWilliam A. Kennington III };
47ff569830SWilliam A. Kennington III 
48ff569830SWilliam A. Kennington III struct ssh_esc_state {
49ff569830SWilliam A. Kennington III 	uint8_t state;
50ff569830SWilliam A. Kennington III };
51ff569830SWilliam A. Kennington III 
5215691c8eSWilliam A. Kennington III struct str_esc_state {
5315691c8eSWilliam A. Kennington III 	const uint8_t *str;
5415691c8eSWilliam A. Kennington III 	size_t pos;
5515691c8eSWilliam A. Kennington III };
5615691c8eSWilliam A. Kennington III 
572bd05186SJeremy Kerr struct console_client {
582bd05186SJeremy Kerr 	int console_sd;
592bd05186SJeremy Kerr 	int fd_in;
602bd05186SJeremy Kerr 	int fd_out;
612bd05186SJeremy Kerr 	bool is_tty;
622bd05186SJeremy Kerr 	struct termios orig_termios;
63ff569830SWilliam A. Kennington III 	enum esc_type esc_type;
64ff569830SWilliam A. Kennington III 	union {
65ff569830SWilliam A. Kennington III 		struct ssh_esc_state ssh;
6615691c8eSWilliam A. Kennington III 		struct str_esc_state str;
67ff569830SWilliam A. Kennington III 	} esc_state;
682bd05186SJeremy Kerr };
692bd05186SJeremy Kerr 
process_ssh_tty(struct console_client * client,const uint8_t * buf,size_t len)70a72711afSAndrew Jeffery static enum process_rc process_ssh_tty(struct console_client *client,
71a72711afSAndrew Jeffery 				       const uint8_t *buf, size_t len)
72ff569830SWilliam A. Kennington III {
73ff569830SWilliam A. Kennington III 	struct ssh_esc_state *esc_state = &client->esc_state.ssh;
74ff569830SWilliam A. Kennington III 	const uint8_t *out_buf = buf;
75ff569830SWilliam A. Kennington III 	int rc;
76ff569830SWilliam A. Kennington III 
77ff569830SWilliam A. Kennington III 	for (size_t i = 0; i < len; ++i) {
78a72711afSAndrew Jeffery 		switch (buf[i]) {
79ff569830SWilliam A. Kennington III 		case '.':
80ff569830SWilliam A. Kennington III 			if (esc_state->state != '~') {
81ff569830SWilliam A. Kennington III 				esc_state->state = '\0';
82ff569830SWilliam A. Kennington III 				break;
83ff569830SWilliam A. Kennington III 			}
848a154357SWilliam A. Kennington III 			return PROCESS_ESC;
85ff569830SWilliam A. Kennington III 		case '~':
86ff569830SWilliam A. Kennington III 			if (esc_state->state != '\r') {
87ff569830SWilliam A. Kennington III 				esc_state->state = '\0';
88ff569830SWilliam A. Kennington III 				break;
89ff569830SWilliam A. Kennington III 			}
90ff569830SWilliam A. Kennington III 			esc_state->state = '~';
91ff569830SWilliam A. Kennington III 			/* We need to print everything to skip the tilde */
92a72711afSAndrew Jeffery 			rc = write_buf_to_fd(client->console_sd, out_buf,
93a72711afSAndrew Jeffery 					     i - (out_buf - buf));
942834c5b1SAndrew Jeffery 			if (rc < 0) {
95ff569830SWilliam A. Kennington III 				return PROCESS_ERR;
962834c5b1SAndrew Jeffery 			}
97ff569830SWilliam A. Kennington III 			out_buf = &buf[i + 1];
98ff569830SWilliam A. Kennington III 			break;
99ff569830SWilliam A. Kennington III 		case '\r':
100ff569830SWilliam A. Kennington III 			esc_state->state = '\r';
101ff569830SWilliam A. Kennington III 			break;
102ff569830SWilliam A. Kennington III 		default:
103ff569830SWilliam A. Kennington III 			esc_state->state = '\0';
104ff569830SWilliam A. Kennington III 		}
105ff569830SWilliam A. Kennington III 	}
106ff569830SWilliam A. Kennington III 
107a72711afSAndrew Jeffery 	rc = write_buf_to_fd(client->console_sd, out_buf,
108a72711afSAndrew Jeffery 			     len - (out_buf - buf));
109ff569830SWilliam A. Kennington III 	return rc < 0 ? PROCESS_ERR : PROCESS_OK;
110ff569830SWilliam A. Kennington III }
1112bd05186SJeremy Kerr 
process_str_tty(struct console_client * client,const uint8_t * buf,size_t len)112a72711afSAndrew Jeffery static enum process_rc process_str_tty(struct console_client *client,
113a72711afSAndrew Jeffery 				       const uint8_t *buf, size_t len)
11415691c8eSWilliam A. Kennington III {
11515691c8eSWilliam A. Kennington III 	struct str_esc_state *esc_state = &client->esc_state.str;
11615691c8eSWilliam A. Kennington III 	enum process_rc prc = PROCESS_OK;
11715691c8eSWilliam A. Kennington III 	size_t i;
11815691c8eSWilliam A. Kennington III 
11915691c8eSWilliam A. Kennington III 	for (i = 0; i < len; ++i) {
1202834c5b1SAndrew Jeffery 		if (buf[i] == esc_state->str[esc_state->pos]) {
12115691c8eSWilliam A. Kennington III 			esc_state->pos++;
1222834c5b1SAndrew Jeffery 		} else {
12315691c8eSWilliam A. Kennington III 			esc_state->pos = 0;
1242834c5b1SAndrew Jeffery 		}
12515691c8eSWilliam A. Kennington III 
12615691c8eSWilliam A. Kennington III 		if (esc_state->str[esc_state->pos] == '\0') {
1278a154357SWilliam A. Kennington III 			prc = PROCESS_ESC;
1285b16dc89SWilliam A. Kennington III 			++i;
12915691c8eSWilliam A. Kennington III 			break;
13015691c8eSWilliam A. Kennington III 		}
13115691c8eSWilliam A. Kennington III 	}
13215691c8eSWilliam A. Kennington III 
1332834c5b1SAndrew Jeffery 	if (write_buf_to_fd(client->console_sd, buf, i) < 0) {
13415691c8eSWilliam A. Kennington III 		return PROCESS_ERR;
1352834c5b1SAndrew Jeffery 	}
13615691c8eSWilliam A. Kennington III 	return prc;
13715691c8eSWilliam A. Kennington III }
13815691c8eSWilliam A. Kennington III 
process_tty(struct console_client * client)1392bd05186SJeremy Kerr static enum process_rc process_tty(struct console_client *client)
1402bd05186SJeremy Kerr {
141ff569830SWilliam A. Kennington III 	uint8_t buf[4096];
1422bd05186SJeremy Kerr 	ssize_t len;
1432bd05186SJeremy Kerr 
1442bd05186SJeremy Kerr 	len = read(client->fd_in, buf, sizeof(buf));
1452834c5b1SAndrew Jeffery 	if (len < 0) {
1462bd05186SJeremy Kerr 		return PROCESS_ERR;
1472834c5b1SAndrew Jeffery 	}
1482834c5b1SAndrew Jeffery 	if (len == 0) {
1492bd05186SJeremy Kerr 		return PROCESS_EXIT;
1502834c5b1SAndrew Jeffery 	}
1512bd05186SJeremy Kerr 
152a72711afSAndrew Jeffery 	switch (client->esc_type) {
153ff569830SWilliam A. Kennington III 	case ESC_TYPE_SSH:
154ff569830SWilliam A. Kennington III 		return process_ssh_tty(client, buf, len);
15515691c8eSWilliam A. Kennington III 	case ESC_TYPE_STR:
15615691c8eSWilliam A. Kennington III 		return process_str_tty(client, buf, len);
157ff569830SWilliam A. Kennington III 	default:
1582bd05186SJeremy Kerr 		return PROCESS_ERR;
159ff569830SWilliam A. Kennington III 	}
1602bd05186SJeremy Kerr }
1612bd05186SJeremy Kerr 
process_console(struct console_client * client)1622bd05186SJeremy Kerr static int process_console(struct console_client *client)
1632bd05186SJeremy Kerr {
1642bd05186SJeremy Kerr 	uint8_t buf[4096];
1655c359cc6SAndrew Jeffery 	ssize_t len;
1665c359cc6SAndrew Jeffery 	int rc;
1672bd05186SJeremy Kerr 
1682bd05186SJeremy Kerr 	len = read(client->console_sd, buf, sizeof(buf));
1692bd05186SJeremy Kerr 	if (len < 0) {
1702bd05186SJeremy Kerr 		warn("Can't read from server");
1712bd05186SJeremy Kerr 		return PROCESS_ERR;
1722bd05186SJeremy Kerr 	}
1732bd05186SJeremy Kerr 	if (len == 0) {
1742bd05186SJeremy Kerr 		fprintf(stderr, "Connection closed\n");
1752bd05186SJeremy Kerr 		return PROCESS_EXIT;
1762bd05186SJeremy Kerr 	}
1772bd05186SJeremy Kerr 
1782bd05186SJeremy Kerr 	rc = write_buf_to_fd(client->fd_out, buf, len);
1792bd05186SJeremy Kerr 	return rc ? PROCESS_ERR : PROCESS_OK;
1802bd05186SJeremy Kerr }
1812bd05186SJeremy Kerr 
1822bd05186SJeremy Kerr /*
1832bd05186SJeremy Kerr  * Setup our local file descriptors for IO: use stdin/stdout, and if we're on a
1842bd05186SJeremy Kerr  * TTY, put it in canonical mode
1852bd05186SJeremy Kerr  */
client_tty_init(struct console_client * client)1862bd05186SJeremy Kerr static int client_tty_init(struct console_client *client)
1872bd05186SJeremy Kerr {
1882bd05186SJeremy Kerr 	struct termios termios;
1892bd05186SJeremy Kerr 	int rc;
1902bd05186SJeremy Kerr 
1912bd05186SJeremy Kerr 	client->fd_in = STDIN_FILENO;
1922bd05186SJeremy Kerr 	client->fd_out = STDOUT_FILENO;
1932bd05186SJeremy Kerr 	client->is_tty = isatty(client->fd_in);
1942bd05186SJeremy Kerr 
1952834c5b1SAndrew Jeffery 	if (!client->is_tty) {
1962bd05186SJeremy Kerr 		return 0;
1972834c5b1SAndrew Jeffery 	}
1982bd05186SJeremy Kerr 
1992bd05186SJeremy Kerr 	rc = tcgetattr(client->fd_in, &termios);
2002bd05186SJeremy Kerr 	if (rc) {
2012bd05186SJeremy Kerr 		warn("Can't get terminal attributes for console");
2022bd05186SJeremy Kerr 		return -1;
2032bd05186SJeremy Kerr 	}
2042bd05186SJeremy Kerr 	memcpy(&client->orig_termios, &termios, sizeof(client->orig_termios));
2052bd05186SJeremy Kerr 	cfmakeraw(&termios);
2062bd05186SJeremy Kerr 
2072bd05186SJeremy Kerr 	rc = tcsetattr(client->fd_in, TCSANOW, &termios);
2082bd05186SJeremy Kerr 	if (rc) {
2092bd05186SJeremy Kerr 		warn("Can't set terminal attributes for console");
2102bd05186SJeremy Kerr 		return -1;
2112bd05186SJeremy Kerr 	}
2122bd05186SJeremy Kerr 
2132bd05186SJeremy Kerr 	return 0;
2142bd05186SJeremy Kerr }
2152bd05186SJeremy Kerr 
client_init(struct console_client * client,struct config * config,const char * console_id)2165ba20b5bSNinad Palsule static int client_init(struct console_client *client, struct config *config,
2175ba20b5bSNinad Palsule 		       const char *console_id)
2182bd05186SJeremy Kerr {
2195ba20b5bSNinad Palsule 	const char *resolved_id = NULL;
2202bd05186SJeremy Kerr 	struct sockaddr_un addr;
2216ed0e4e7SAndrew Jeffery 	socket_path_t path;
2225e7c0786SAndrew Jeffery 	ssize_t len;
2232bd05186SJeremy Kerr 	int rc;
2242bd05186SJeremy Kerr 
2252bd05186SJeremy Kerr 	client->console_sd = socket(AF_UNIX, SOCK_STREAM, 0);
2262bd05186SJeremy Kerr 	if (!client->console_sd) {
2272bd05186SJeremy Kerr 		warn("Can't open socket");
2282bd05186SJeremy Kerr 		return -1;
2292bd05186SJeremy Kerr 	}
2302bd05186SJeremy Kerr 
2315ba20b5bSNinad Palsule 	/* Get the console id */
2325ba20b5bSNinad Palsule 	resolved_id = config_resolve_console_id(config, console_id);
2335ba20b5bSNinad Palsule 
2342bd05186SJeremy Kerr 	memset(&addr, 0, sizeof(addr));
2352bd05186SJeremy Kerr 	addr.sun_family = AF_UNIX;
2365ba20b5bSNinad Palsule 	len = console_socket_path(addr.sun_path, resolved_id);
2375e7c0786SAndrew Jeffery 	if (len < 0) {
2382834c5b1SAndrew Jeffery 		if (errno) {
2395e7c0786SAndrew Jeffery 			warn("Failed to configure socket: %s", strerror(errno));
2402834c5b1SAndrew Jeffery 		} else {
2415e7c0786SAndrew Jeffery 			warn("Socket name length exceeds buffer limits");
2422834c5b1SAndrew Jeffery 		}
2435e7c0786SAndrew Jeffery 		goto cleanup;
2442bd05186SJeremy Kerr 	}
2452bd05186SJeremy Kerr 
2465e7c0786SAndrew Jeffery 	rc = connect(client->console_sd, (struct sockaddr *)&addr,
2475e7c0786SAndrew Jeffery 		     sizeof(addr) - sizeof(addr.sun_path) + len);
2482834c5b1SAndrew Jeffery 	if (!rc) {
2492bd05186SJeremy Kerr 		return 0;
2502834c5b1SAndrew Jeffery 	}
2515e7c0786SAndrew Jeffery 
2526ed0e4e7SAndrew Jeffery 	console_socket_path_readable(&addr, len, path);
2536ed0e4e7SAndrew Jeffery 	warn("Can't connect to console server '@%s'", path);
2545e7c0786SAndrew Jeffery cleanup:
2555e7c0786SAndrew Jeffery 	close(client->console_sd);
2565e7c0786SAndrew Jeffery 	return -1;
2572bd05186SJeremy Kerr }
2582bd05186SJeremy Kerr 
client_fini(struct console_client * client)2592bd05186SJeremy Kerr static void client_fini(struct console_client *client)
2602bd05186SJeremy Kerr {
2612834c5b1SAndrew Jeffery 	if (client->is_tty) {
2622bd05186SJeremy Kerr 		tcsetattr(client->fd_in, TCSANOW, &client->orig_termios);
2632834c5b1SAndrew Jeffery 	}
2642bd05186SJeremy Kerr 	close(client->console_sd);
2652bd05186SJeremy Kerr }
2662bd05186SJeremy Kerr 
main(int argc,char * argv[])26715691c8eSWilliam A. Kennington III int main(int argc, char *argv[])
2682bd05186SJeremy Kerr {
269b70f8713SAndrew Jeffery 	struct console_client _client;
270b70f8713SAndrew Jeffery 	struct console_client *client;
2712bd05186SJeremy Kerr 	struct pollfd pollfds[2];
2728a154357SWilliam A. Kennington III 	enum process_rc prc = PROCESS_OK;
27371e7a249SAndrew Jeffery 	const char *config_path = NULL;
27471e7a249SAndrew Jeffery 	struct config *config = NULL;
2759a8f30ecSAndrew Jeffery 	const char *console_id = NULL;
27671e7a249SAndrew Jeffery 	const uint8_t *esc = NULL;
2772bd05186SJeremy Kerr 	int rc;
2782bd05186SJeremy Kerr 
2792bd05186SJeremy Kerr 	client = &_client;
2802bd05186SJeremy Kerr 	memset(client, 0, sizeof(*client));
281ff569830SWilliam A. Kennington III 	client->esc_type = ESC_TYPE_SSH;
2822bd05186SJeremy Kerr 
28315691c8eSWilliam A. Kennington III 	for (;;) {
28471e7a249SAndrew Jeffery 		rc = getopt(argc, argv, "c:e:i:");
2852834c5b1SAndrew Jeffery 		if (rc == -1) {
28615691c8eSWilliam A. Kennington III 			break;
2872834c5b1SAndrew Jeffery 		}
28815691c8eSWilliam A. Kennington III 
28915691c8eSWilliam A. Kennington III 		switch (rc) {
29071e7a249SAndrew Jeffery 		case 'c':
29171e7a249SAndrew Jeffery 			if (optarg[0] == '\0') {
29271e7a249SAndrew Jeffery 				fprintf(stderr, "Config str cannot be empty\n");
29371e7a249SAndrew Jeffery 				return EXIT_FAILURE;
29471e7a249SAndrew Jeffery 			}
29571e7a249SAndrew Jeffery 			config_path = optarg;
29671e7a249SAndrew Jeffery 			break;
29715691c8eSWilliam A. Kennington III 		case 'e':
29815691c8eSWilliam A. Kennington III 			if (optarg[0] == '\0') {
29915691c8eSWilliam A. Kennington III 				fprintf(stderr, "Escape str cannot be empty\n");
30015691c8eSWilliam A. Kennington III 				return EXIT_FAILURE;
30115691c8eSWilliam A. Kennington III 			}
30271e7a249SAndrew Jeffery 			esc = (const uint8_t *)optarg;
30315691c8eSWilliam A. Kennington III 			break;
304ddf2ab7cSAndrew Jeffery 		case 'i':
305ddf2ab7cSAndrew Jeffery 			if (optarg[0] == '\0') {
306a72711afSAndrew Jeffery 				fprintf(stderr,
307a72711afSAndrew Jeffery 					"Socket ID str cannot be empty\n");
308ddf2ab7cSAndrew Jeffery 				return EXIT_FAILURE;
309ddf2ab7cSAndrew Jeffery 			}
3109a8f30ecSAndrew Jeffery 			console_id = optarg;
311ddf2ab7cSAndrew Jeffery 			break;
31215691c8eSWilliam A. Kennington III 		default:
31315691c8eSWilliam A. Kennington III 			fprintf(stderr,
31415691c8eSWilliam A. Kennington III 				"Usage: %s "
315ddf2ab7cSAndrew Jeffery 				"[-e <escape sequence>]"
3169a8f30ecSAndrew Jeffery 				"[-i <console ID>]"
31771e7a249SAndrew Jeffery 				"[-c <config>]\n",
31815691c8eSWilliam A. Kennington III 				argv[0]);
31915691c8eSWilliam A. Kennington III 			return EXIT_FAILURE;
32015691c8eSWilliam A. Kennington III 		}
32115691c8eSWilliam A. Kennington III 	}
32215691c8eSWilliam A. Kennington III 
32371e7a249SAndrew Jeffery 	if (config_path) {
32471e7a249SAndrew Jeffery 		config = config_init(config_path);
32571e7a249SAndrew Jeffery 		if (!config) {
32671e7a249SAndrew Jeffery 			warnx("Can't read configuration, exiting.");
32771e7a249SAndrew Jeffery 			return EXIT_FAILURE;
32871e7a249SAndrew Jeffery 		}
32971e7a249SAndrew Jeffery 
3302834c5b1SAndrew Jeffery 		if (!esc) {
331a72711afSAndrew Jeffery 			esc = (const uint8_t *)config_get_value(
332a72711afSAndrew Jeffery 				config, "escape-sequence");
3332834c5b1SAndrew Jeffery 		}
3342834c5b1SAndrew Jeffery 	}
33571e7a249SAndrew Jeffery 
33671e7a249SAndrew Jeffery 	if (esc) {
33771e7a249SAndrew Jeffery 		client->esc_type = ESC_TYPE_STR;
33871e7a249SAndrew Jeffery 		client->esc_state.str.str = esc;
33971e7a249SAndrew Jeffery 	}
34071e7a249SAndrew Jeffery 
3415ba20b5bSNinad Palsule 	rc = client_init(client, config, console_id);
3422834c5b1SAndrew Jeffery 	if (rc) {
34371e7a249SAndrew Jeffery 		goto out_config_fini;
3442834c5b1SAndrew Jeffery 	}
3452bd05186SJeremy Kerr 
3462bd05186SJeremy Kerr 	rc = client_tty_init(client);
3472834c5b1SAndrew Jeffery 	if (rc) {
34871e7a249SAndrew Jeffery 		goto out_client_fini;
3492834c5b1SAndrew Jeffery 	}
3502bd05186SJeremy Kerr 
3512bd05186SJeremy Kerr 	for (;;) {
3522bd05186SJeremy Kerr 		pollfds[0].fd = client->fd_in;
3532bd05186SJeremy Kerr 		pollfds[0].events = POLLIN;
3542bd05186SJeremy Kerr 		pollfds[1].fd = client->console_sd;
3552bd05186SJeremy Kerr 		pollfds[1].events = POLLIN;
3562bd05186SJeremy Kerr 
3572bd05186SJeremy Kerr 		rc = poll(pollfds, 2, -1);
3582bd05186SJeremy Kerr 		if (rc < 0) {
3592bd05186SJeremy Kerr 			warn("Poll failure");
3602bd05186SJeremy Kerr 			break;
3612bd05186SJeremy Kerr 		}
3622bd05186SJeremy Kerr 
3632834c5b1SAndrew Jeffery 		if (pollfds[0].revents) {
3642bd05186SJeremy Kerr 			prc = process_tty(client);
3652834c5b1SAndrew Jeffery 		}
3662bd05186SJeremy Kerr 
3672834c5b1SAndrew Jeffery 		if (prc == PROCESS_OK && pollfds[1].revents) {
3682bd05186SJeremy Kerr 			prc = process_console(client);
3692834c5b1SAndrew Jeffery 		}
3702bd05186SJeremy Kerr 
3712bd05186SJeremy Kerr 		rc = (prc == PROCESS_ERR) ? -1 : 0;
3722834c5b1SAndrew Jeffery 		if (prc != PROCESS_OK) {
3732bd05186SJeremy Kerr 			break;
3742bd05186SJeremy Kerr 		}
3752834c5b1SAndrew Jeffery 	}
3762bd05186SJeremy Kerr 
37771e7a249SAndrew Jeffery out_client_fini:
3782bd05186SJeremy Kerr 	client_fini(client);
37971e7a249SAndrew Jeffery 
38071e7a249SAndrew Jeffery out_config_fini:
3812834c5b1SAndrew Jeffery 	if (config_path) {
38271e7a249SAndrew Jeffery 		config_fini(config);
3832834c5b1SAndrew Jeffery 	}
38471e7a249SAndrew Jeffery 
3852834c5b1SAndrew Jeffery 	if (prc == PROCESS_ESC) {
3868a154357SWilliam A. Kennington III 		return EXIT_ESCAPE;
3872834c5b1SAndrew Jeffery 	}
3882bd05186SJeremy Kerr 	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
3892bd05186SJeremy Kerr }
390