diff --git a/src/tools/fs_shell/Jamfile b/src/tools/fs_shell/Jamfile index 68e2e3c062..96ab163d24 100644 --- a/src/tools/fs_shell/Jamfile +++ b/src/tools/fs_shell/Jamfile @@ -11,6 +11,18 @@ UseHeaders [ FDirName $(HAIKU_TOP) headers build os support ] : true ; UsePrivateHeaders fs_shell ; UsePrivateHeaders shared ; +# platform specific sources +local fsShellCommandSources ; +local externalCommandsSources ; +if $(HOST_PLATFORM_BEOS_COMPATIBLE) { + fsShellCommandSources = fs_shell_command_beos.cpp ; + externalCommandsSources = external_commands_beos.cpp ; +} else { + fsShellCommandSources = fs_shell_command_unix.cpp ; + externalCommandsSources = external_commands_unix.cpp ; +} + + BuildPlatformStaticLibrary fs_shell.a : atomic.cpp block_cache.cpp @@ -39,5 +51,11 @@ BuildPlatformStaticLibrary fs_shell.a : unistd.cpp vfs.cpp + $(externalCommandsSources) + fssh.cpp ; + +BuildPlatformMain fs_shell_command + : fs_shell_command.cpp $(fsShellCommandSources) + : $(HOST_LIBSUPC++) ; diff --git a/src/tools/fs_shell/external_commands.h b/src/tools/fs_shell/external_commands.h new file mode 100644 index 0000000000..0c6084eb41 --- /dev/null +++ b/src/tools/fs_shell/external_commands.h @@ -0,0 +1,21 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_EXTERNAL_COMMANDS_H +#define _FSSH_EXTERNAL_COMMANDS_H + + +namespace FSShell { + + +bool get_external_command(char* input, int len); +void reply_to_external_command(int result); +void external_command_cleanup(); + + +} // namespace FSShell + + +#endif // _FSSH_EXTERNAL_COMMANDS_H diff --git a/src/tools/fs_shell/external_commands_beos.cpp b/src/tools/fs_shell/external_commands_beos.cpp new file mode 100644 index 0000000000..519bc13378 --- /dev/null +++ b/src/tools/fs_shell/external_commands_beos.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include + +#include "external_commands.h" +#include "fs_shell_command_beos.h" + +static port_id sReplyPort = -1; + +static port_id +get_command_port() +{ + static port_id port = -1; + static bool initialized = false; + + if (!initialized) { + port = create_port(10, kFSShellCommandPort); + initialized = true; + } + + return port; +} + + +bool +get_external_command(char *input, int len) +{ + // get/create the port + port_id port = get_command_port(); + if (port < 0) { + fprintf(stderr, "Failed to create command port: %s\n", strerror(port)); + return false; + } + + while (true) { + // read a message + external_command_message message; + ssize_t bytesRead; + do { + int32 code; + bytesRead = read_port(port, &code, &message, sizeof(message)); + } while (bytesRead == B_INTERRUPTED); + + if (bytesRead < 0) { + fprintf(stderr, "Reading from port failed: %s\n", + strerror(bytesRead)); + return false; + } + + // get the len of the command + int commandLen = (char*)&message + bytesRead - message.command; + if (commandLen <= 1) { + fprintf(stderr, "No command given.\n"); + continue; + } + if (commandLen > len) { + fprintf(stderr, "Command too long. Ignored.\n"); + continue; + } + + // copy the command + memcpy(input, message.command, commandLen); + input[len - 1] = '\0'; // always NULL-terminate + sReplyPort = message.reply_port; + return true; + } +} + + +void +reply_to_external_command(int result) +{ + if (sReplyPort >= 0) { + // prepare the message + external_command_reply reply; + reply.error = result; + + // send the reply + status_t error; + do { + error = write_port(sReplyPort, 0, &reply, sizeof(reply)); + } while (error == B_INTERRUPTED); + sReplyPort = -1; + + if (error != B_OK) { + fprintf(stderr, "Failed to send command result to reply port: %s\n", + strerror(error)); + } + } +} + + +void +external_command_cleanup() +{ + // The port will be deleted automatically when the team exits. +} diff --git a/src/tools/fs_shell/external_commands_unix.cpp b/src/tools/fs_shell/external_commands_unix.cpp new file mode 100644 index 0000000000..cc8dc185af --- /dev/null +++ b/src/tools/fs_shell/external_commands_unix.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include + +#include "external_commands.h" +#include "fs_shell_command_unix.h" + + +static int sClientConnection = -1; + + +static int +get_command_socket() +{ + static int fd = -1; + static bool initialized = false; + if (!initialized) { + // get the listener socket + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + // bind it to the port + sockaddr_un addr; + unlink(kFSShellCommandSocketAddress); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, kFSShellCommandSocketAddress); + int addrLen = addr.sun_path + strlen(addr.sun_path) + 1 - (char*)&addr; + if (bind(fd, (sockaddr*)&addr, addrLen) < 0) { + close(fd); + return -1; + } + + // start listening + if (listen(fd, 1) < 0) { + close(fd); + return -1; + } + + initialized = true; + } + + return fd; +} + + +static int +get_client_connection() +{ + if (sClientConnection >= 0) + return sClientConnection; + + // get the listener socket + int commandFD = get_command_socket(); + if (commandFD < 0) + return -1; + + // accept a connection + do { + sockaddr_un addr; + socklen_t addrLen = sizeof(addr); + sClientConnection = accept(commandFD, (sockaddr*)&addr, &addrLen); + } while (sClientConnection < 0 && errno == EINTR); + + return sClientConnection; +} + + +static void +close_client_connection() +{ + if (sClientConnection >= 0) { + close(sClientConnection); + sClientConnection = -1; + } +} + + +static bool +read_data(int fd, void* _buffer, size_t toRead) +{ + char* buffer = (char*)_buffer; + + ssize_t bytesRead = 0; + while (toRead > 0 && !(bytesRead < 0 && errno != EINTR)) { + bytesRead = read(fd, buffer, toRead); + if (bytesRead == 0) + break; + if (bytesRead > 0) { + buffer += bytesRead; + toRead -= bytesRead; + } + } + + return (toRead == 0); +} + + +bool +FSShell::get_external_command(char *input, int len) +{ + do { + // get a connection + int connection = get_client_connection(); + if (connection < 0) + return false; + + // read command message + external_command_message message; + if (!read_data(connection, &message, sizeof(message))) { + // that usually means the connection was closed + close_client_connection(); + continue; + } + + // check command length + if (message.command_length >= (unsigned)len) { + fprintf(stderr, "Error: Command too long!\n"); + close_client_connection(); + continue; + } + + // read the command + if (!read_data(connection, input, message.command_length)) { + fprintf(stderr, "Error: Reading from connection failed: " + "%s\n", strerror(errno)); + close_client_connection(); + continue; + } + + // null-terminate + input[message.command_length] = '\0'; + + return true; + + } while (true); +} + + +void +FSShell::reply_to_external_command(int result) +{ + if (sClientConnection < 0) + return; + + // prepare the reply + external_command_reply reply; + reply.error = result; + + // send the reply + int toWrite = sizeof(reply); + char *replyBuffer = (char*)&reply; + ssize_t bytesWritten; + do { + bytesWritten = write(sClientConnection, replyBuffer, toWrite); + if (bytesWritten > 0) { + replyBuffer += bytesWritten; + toWrite -= bytesWritten; + } + } while (toWrite > 0 && !(bytesWritten < 0 && errno != EINTR)); + + // connection may be broken: discard it + if (bytesWritten < 0) + close_client_connection(); +} + + +void +FSShell::external_command_cleanup() +{ + unlink(kFSShellCommandSocketAddress); +} diff --git a/src/tools/fs_shell/fs_shell_command.cpp b/src/tools/fs_shell/fs_shell_command.cpp new file mode 100644 index 0000000000..c6f7981d5a --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fs_shell_command.h" + +#include +#include +#include + + +static void +add_char(char *&buffer, int &bufferSize, char c) +{ + if (bufferSize <= 0) { + fprintf(stderr, "Error: Command line too long\n"); + exit(1); + } + + *buffer = c; + buffer++; + bufferSize--; +} + + +static void +prepare_command_string(const char *const *argv, int argc, char *buffer, + int bufferSize) +{ + for (int argi = 0; argi < argc; argi++) { + const char *arg = argv[argi]; + + if (argi > 0) + add_char(buffer, bufferSize, ' '); + + while (*arg) { + if (strchr(" \\", *arg)) + add_char(buffer, bufferSize, '\\'); + add_char(buffer, bufferSize, *arg); + arg++; + } + } + + add_char(buffer, bufferSize, '\0'); +} + + +int +main(int argc, const char *const *argv) +{ + if (argc < 2) { + fprintf(stderr, "Error: No command given.\n"); + exit(1); + } + + // prepare the command string + char command[102400]; + prepare_command_string(argv + 1, argc - 1, command, sizeof(command)); + + // send the command + int result; + if (!send_external_command(command, &result)) + exit(1); + + // evaluate result + if (result != 0) { + fprintf(stderr, "Error: Command failed: %s\n", strerror(result)); + fprintf(stderr, "Error: Command was:\n %s\n", command); + exit(1); + } + + return 0; +} + diff --git a/src/tools/fs_shell/fs_shell_command.h b/src/tools/fs_shell/fs_shell_command.h new file mode 100644 index 0000000000..c9df261808 --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command.h @@ -0,0 +1,13 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_FS_SHELL_COMMAND_H +#define _FSSH_FS_SHELL_COMMAND_H + + +bool send_external_command(const char* command, int* result); + + +#endif // _FSSH_FS_SHELL_COMMAND_H diff --git a/src/tools/fs_shell/fs_shell_command_beos.cpp b/src/tools/fs_shell/fs_shell_command_beos.cpp new file mode 100644 index 0000000000..fe5b3f27dc --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command_beos.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fs_shell_command_beos.h" + +#include +#include + +#include + +#include "fs_shell_command.h" + + +bool +send_external_command(const char *command, int *result) +{ + external_command_message message; + strncpy(message.command, command, sizeof(message.command)); + + // find the command port + port_id commandPort = find_port(kFSShellCommandPort); + if (commandPort < 0) { + fprintf(stderr, "Error: Couldn't find fs_shell command port.\n"); + return false; + } + + // create a reply port + port_id replyPort = create_port(1, "fs shell reply port"); + if (replyPort < 0) { + fprintf(stderr, "Error: Failed to create a reply port: %s\n", + strerror(replyPort)); + return false; + } + message.reply_port = replyPort; + + // send the command message + status_t error; + do { + error = write_port(commandPort, 0, &message, sizeof(message)); + } while (error == B_INTERRUPTED); + + if (error != B_OK) { + fprintf(stderr, "Error: Failed to send command: %s\n", strerror(error)); + return false; + } + + // wait for the reply + external_command_reply reply; + ssize_t bytesRead; + do { + int32 code; + bytesRead = read_port(replyPort, &code, &reply, sizeof(reply)); + } while (bytesRead == B_INTERRUPTED); + + if (bytesRead < 0) { + fprintf(stderr, "Error: Failed to read reply from fs_shell: %s\n", + strerror(bytesRead)); + return false; + } + + *result = reply.error; + return true; +} + diff --git a/src/tools/fs_shell/fs_shell_command_beos.h b/src/tools/fs_shell/fs_shell_command_beos.h new file mode 100644 index 0000000000..e90682c5a6 --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command_beos.h @@ -0,0 +1,20 @@ +/* + * Copyright 2005, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef FS_SHELL_COMMAND_BEOS_H +#define FS_SHELL_COMMAND_BEOS_H + +static const char *kFSShellCommandPort = "fs shell command port"; + +struct external_command_message { + port_id reply_port; + char command[20480]; +// TODO: Increase command size and transmit only as much as necessary. +}; + +struct external_command_reply { + int error; +}; + +#endif // FS_SHELL_COMMAND_BEOS_H diff --git a/src/tools/fs_shell/fs_shell_command_unix.cpp b/src/tools/fs_shell/fs_shell_command_unix.cpp new file mode 100644 index 0000000000..862cecdbe4 --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command_unix.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fs_shell_command_unix.h" + +#include +#include +#include +#include +#include +#include + +#include "fs_shell_command.h" + + +static bool +write_data(int fd, const void* _buffer, size_t toWrite) +{ + const char* buffer = (const char*)_buffer; + + ssize_t bytesWritten; + do { + bytesWritten = write(fd, buffer, toWrite); + if (bytesWritten > 0) { + buffer += bytesWritten; + toWrite -= bytesWritten; + } + } while (toWrite > 0 && !(bytesWritten < 0 && errno != EINTR)); + + return (bytesWritten >= 0); +} + + +bool +send_external_command(const char *command, int *result) +{ + external_command_message message; + message.command_length = strlen(command); + + // create a socket + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Error: Failed to open unix socket: %s\n", + strerror(errno)); + return false; + } + + // connect to the fs_shell + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, kFSShellCommandSocketAddress); + int addrLen = addr.sun_path + strlen(addr.sun_path) + 1 - (char*)&addr; + if (connect(fd, (sockaddr*)&addr, addrLen) < 0) { + fprintf(stderr, "Error: Failed to open connection to FS shell: %s\n", + strerror(errno)); + close(fd); + return false; + } + + // send the command message and the command + if (!write_data(fd, &message, sizeof(message)) + || !write_data(fd, command, message.command_length)) { + fprintf(stderr, "Error: Writing to fs_shell failed: %s\n", + strerror(errno)); + close(fd); + return false; + } + + // read the reply + external_command_reply reply; + int toRead = sizeof(reply); + char *replyBuffer = (char*)&reply; + while (toRead > 0) { + int bytesRead = read(fd, replyBuffer, toRead); + if (bytesRead < 0) { + if (errno == EINTR) { + continue; + } else { + fprintf(stderr, "Error: Failed to read reply from fs_shell: " + "%s\n", strerror(errno)); + close(fd); + return false; + } + } + + if (bytesRead == 0) { + fprintf(stderr, "Error: Unexpected end of fs_shell reply. Was " + "still expecting %d bytes\n", toRead); + return false; + } + + replyBuffer += bytesRead; + toRead -= bytesRead; + } + + *result = reply.error; + return true; +} + diff --git a/src/tools/fs_shell/fs_shell_command_unix.h b/src/tools/fs_shell/fs_shell_command_unix.h new file mode 100644 index 0000000000..ac25cc0581 --- /dev/null +++ b/src/tools/fs_shell/fs_shell_command_unix.h @@ -0,0 +1,21 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_FS_SHELL_COMMAND_UNIX_H +#define _FSSH_FS_SHELL_COMMAND_UNIX_H + + +static const char *kFSShellCommandSocketAddress = "/tmp/fs_shell_commands"; + +struct external_command_message { + unsigned command_length; +}; + +struct external_command_reply { + int error; +}; + + +#endif // _FSSH_FS_SHELL_COMMAND_UNIX_H diff --git a/src/tools/fs_shell/fssh.cpp b/src/tools/fs_shell/fssh.cpp index ae4ed65575..4aaf9e04ea 100644 --- a/src/tools/fs_shell/fssh.cpp +++ b/src/tools/fs_shell/fssh.cpp @@ -15,6 +15,7 @@ #include #include "command_cp.h" +#include "external_commands.h" #include "fd.h" #include "fssh_dirent.h" #include "fssh_errno.h" @@ -38,6 +39,10 @@ extern fssh_file_system_module_info gRootFileSystem; const char* kMountPoint = "/myfs"; +// command line args +static int sArgc; +static const char* const* sArgv; + static fssh_status_t init_kernel() @@ -49,8 +54,7 @@ init_kernel() if (error != FSSH_B_OK) { fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error)); return error; - } else - printf("module_init() succeeded!\n"); + } // register built-in modules, i.e. the rootfs and the client FS register_builtin_module(&gRootFileSystem.info); @@ -62,8 +66,7 @@ init_kernel() if (error != FSSH_B_OK) { fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error)); return error; - } else - printf("VFS initialized successfully!\n"); + } // init kernel IO context gKernelIOContext = (io_context*)vfs_new_io_context(NULL); @@ -77,16 +80,14 @@ init_kernel() if (rootDev < 0) { fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev)); return rootDev; - } else - printf("mounted rootfs successfully!\n"); + } // set cwd to "/" error = _kern_setcwd(-1, "/"); if (error != FSSH_B_OK) { fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error)); return error; - } else - printf("set cwd successfully!\n"); + } // create mount point for the client FS error = _kern_create_dir(-1, kMountPoint, 0775); @@ -94,8 +95,7 @@ init_kernel() fprintf(stderr, "creating mount point failed: %s\n", fssh_strerror(error)); return error; - } else - printf("mount point created successfully!\n"); + } return FSSH_B_OK; } @@ -1036,15 +1036,20 @@ read_command_line(char* buffer, int bufferSize) static void -input_loop() +input_loop(bool interactive) { static const int kInputBufferSize = 100 * 1024; char* inputBuffer = new char[kInputBufferSize]; for (;;) { // read command line - if (!read_command_line(inputBuffer, kInputBufferSize)) - break; + if (interactive) { + if (!read_command_line(inputBuffer, kInputBufferSize)) + break; + } else { + if (!get_external_command(inputBuffer, kInputBufferSize)) + break; + } // construct argv vector ArgVector argVector; @@ -1062,14 +1067,79 @@ input_loop() } int result = command->Do(argc, argv); - if (result == COMMAND_RESULT_EXIT) + if (result == COMMAND_RESULT_EXIT) { + reply_to_external_command(0); break; + } + + reply_to_external_command(fssh_to_host_error(result)); } + if (!interactive) + external_command_cleanup(); + delete[] inputBuffer; } +static int +standard_session(const char* device, const char* fsName, bool interactive) +{ + // mount FS + fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); + if (fsDev < 0) { + fprintf(stderr, "Error: Mounting FS failed: %s\n", + fssh_strerror(fsDev)); + return 1; + } + + // register commands + register_commands(); + + // process commands + input_loop(interactive); + + // unmount FS + _kern_setcwd(-1, "/"); // avoid a "busy" vnode + fssh_status_t error = _kern_unmount(kMountPoint, 0); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: Unmounting FS failed: %s\n", + fssh_strerror(error)); + return 1; + } + + return 0; +} + + +static int +initialization_session(const char* device, const char* fsName, + const char* initParameters) +{ + fprintf(stderr, "initialization not implemented yet\n"); + return 1; +} + + +static void +print_usage(bool error) +{ + fprintf((error ? stderr : stdout), + "Usage: %s [-n] \n" + " %s --initialize [-n] [ ]\n", + sArgv[0], sArgv[0] + ); +} + + +static void +print_usage_and_exit(bool error) +{ + print_usage(error); + exit(error ? 1 : 0); +} + + } // namespace FSShell @@ -1079,16 +1149,46 @@ using namespace FSShell; int main(int argc, const char* const* argv) { - if (argc != 2) { - fprintf(stderr, "Usage: ...\n"); - return 1; + sArgc = argc; + sArgv = argv; + + // process arguments + bool interactive = true; + bool initialize = false; + const char* device = NULL; + const char* initParameters = NULL; + + // eat options + int argi = 1; + while (argi < argc && argv[argi][0] == '-') { + const char* arg = argv[argi++]; + if (strcmp(arg, "--help") == 0) { + print_usage_and_exit(false); + } else if (strcmp(arg, "--initialize") == 0) { + initialize = true; + } else if (strcmp(arg, "-n") == 0) { + interactive = false; + } else { + print_usage_and_exit(true); + } } - const char* device = argv[1]; + // get device + if (argi >= argc) + print_usage_and_exit(true); + device = argv[argi++]; + + // get init parameters + if (initialize && argi < argc) + initParameters = argv[argi++]; + + // if more parameters are excess + if (argi < argc) + print_usage_and_exit(true); // get FS module if (!modules[0]) { - fprintf(stderr, "Couldn't find FS module!\n"); + fprintf(stderr, "Error: Couldn't find FS module!\n"); return 1; } const char* fsName = modules[0]->name; @@ -1098,34 +1198,17 @@ main(int argc, const char* const* argv) // init kernel error = init_kernel(); if (error != FSSH_B_OK) { - fprintf(stderr, "initializing kernel failed: %s\n", + fprintf(stderr, "Error: Initializing kernel failed: %s\n", fssh_strerror(error)); return error; - } else - printf("kernel initialized successfully!\n"); + } - // mount FS - fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); - if (fsDev < 0) { - fprintf(stderr, "mounting FS failed: %s\n", fssh_strerror(fsDev)); - return 1; - } else - printf("mounted FS successfully!\n"); + // start the action + int result; + if (initialize) + result = initialization_session(device, fsName, initParameters); + else + result = standard_session(device, fsName, interactive); - // register commands - register_commands(); - - // process commands - input_loop(); - - // unmount FS - _kern_setcwd(-1, "/"); // avoid a "busy" vnode - error = _kern_unmount(kMountPoint, 0); - if (error != FSSH_B_OK) { - fprintf(stderr, "unmounting FS failed: %s\n", fssh_strerror(error)); - return error; - } else - printf("FS unmounted successfully!\n"); - - return 0; + return result; }