blob: d728d38edf165026219c079c2d00c07e505417bc [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <lib/fdio/spawn.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#undef CAN_CLONE_SOCKETS
static const char* sa_to_str(const struct sockaddr* sa, char* str,
size_t strlen) {
if (sa->sa_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
return inet_ntop(AF_INET, &sin->sin_addr, str, strlen);
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
return inet_ntop(AF_INET6, &sin6->sin6_addr, str, strlen);
} else {
return NULL;
}
}
const char* kProgram = "/bin/passfdtest";
static int server(const char* service) {
int16_t port = atoi(service);
printf("listen on port %d\n", port);
int s = socket(AF_INET6, SOCK_STREAM, 0);
if (s < 0) {
printf("socket failed (errno = %d)\n", errno);
return -1;
}
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any; // works with IPv4
addr.sin6_port = htons(port);
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
printf("bind failed (errno = %d)\n", errno);
return -1;
}
if (listen(s, 1) < 0) {
printf("listen failed\n");
return -1;
}
printf("waiting for a connection on port %d...\n", port);
socklen_t addrlen = sizeof(addr);
int conn = accept(s, (struct sockaddr*)&addr, &addrlen);
if (conn < 0) {
close(s);
printf("accept failed (errno = %d)\n", errno);
return -1;
}
char str[INET6_ADDRSTRLEN];
printf("connected from %s\n",
sa_to_str((struct sockaddr*)&addr, str, sizeof(str)));
const char* argv[] = {kProgram, "ECHO", NULL};
fdio_spawn_action_t actions[] = {
#ifdef CAN_CLONE_SOCKETS
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = conn, .target_fd = STDIN_FILENO}},
{.action = FDIO_SPAWN_ACTION_TRANSFER_FD,
.fd = {.local_fd = conn, .target_fd = STDOUT_FILENO}},
#else
{.action = FDIO_SPAWN_ACTION_TRANSFER_FD,
.fd = {.local_fd = conn, .target_fd = STDIN_FILENO}},
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = STDOUT_FILENO, .target_fd = STDOUT_FILENO}},
#endif
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = STDERR_FILENO, .target_fd = STDERR_FILENO}},
};
size_t actions_count = sizeof(actions) / sizeof(actions[0]);
zx_handle_t proc = ZX_HANDLE_INVALID;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_status_t status = fdio_spawn_etc(
ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO,
kProgram, argv, NULL, actions_count, actions, &proc, err_msg);
if (status < 0) {
fprintf(stderr, "error from fdio_spawn_etc: %s\n", err_msg);
return -1;
}
printf("launched %s %s, waiting for it to exit...\n", argv[0], argv[1]);
zx_signals_t observed;
zx_object_wait_one(proc, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, &observed);
printf("child exited.\n");
close(s);
return 0;
}
int echo() {
fprintf(stderr, "ECHO starting\n");
#ifndef CAN_CLONE_SOCKETS
close(STDOUT_FILENO);
dup2(STDIN_FILENO, STDOUT_FILENO);
#endif
for (;;) {
char c;
int l = read(STDIN_FILENO, &c, 1);
if (l == 0) {
fprintf(stderr, "ECHO stdin EOF\n");
break;
}
if (l < 0) {
fprintf(stderr, "ECHO error reading: %s\n", strerror(errno));
break;
}
c = toupper(c);
l = write(STDOUT_FILENO, &c, 1);
if (l == 0) {
fprintf(stderr, "ECHO stdout EOF\n");
break;
}
if (l < 0) {
fprintf(stderr, "ECHO error writing: %s\n", strerror(errno));
break;
}
}
fprintf(stderr, "ECHO exiting\n");
return 0;
}
void usage(void) {
printf("usage: passfdtest <port>\n");
printf(" passfdtest ECHO\n");
}
int main(int argc, char** argv) {
if (argc != 2) {
usage();
return -1;
}
if (!strcmp(argv[1], "ECHO")) {
return echo();
}
return server(argv[1]);
}