1195e14d0SDaniel P. Berrange /* 2195e14d0SDaniel P. Berrange * QEMU I/O channels external command driver 3195e14d0SDaniel P. Berrange * 4195e14d0SDaniel P. Berrange * Copyright (c) 2015 Red Hat, Inc. 5195e14d0SDaniel P. Berrange * 6195e14d0SDaniel P. Berrange * This library is free software; you can redistribute it and/or 7195e14d0SDaniel P. Berrange * modify it under the terms of the GNU Lesser General Public 8195e14d0SDaniel P. Berrange * License as published by the Free Software Foundation; either 9195e14d0SDaniel P. Berrange * version 2 of the License, or (at your option) any later version. 10195e14d0SDaniel P. Berrange * 11195e14d0SDaniel P. Berrange * This library is distributed in the hope that it will be useful, 12195e14d0SDaniel P. Berrange * but WITHOUT ANY WARRANTY; without even the implied warranty of 13195e14d0SDaniel P. Berrange * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14195e14d0SDaniel P. Berrange * Lesser General Public License for more details. 15195e14d0SDaniel P. Berrange * 16195e14d0SDaniel P. Berrange * You should have received a copy of the GNU Lesser General Public 17195e14d0SDaniel P. Berrange * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18195e14d0SDaniel P. Berrange * 19195e14d0SDaniel P. Berrange */ 20195e14d0SDaniel P. Berrange 21*cae9fc56SPeter Maydell #include "qemu/osdep.h" 22195e14d0SDaniel P. Berrange #include "io/channel-command.h" 23195e14d0SDaniel P. Berrange #include "io/channel-watch.h" 24195e14d0SDaniel P. Berrange #include "qemu/sockets.h" 25195e14d0SDaniel P. Berrange #include "trace.h" 26195e14d0SDaniel P. Berrange 27195e14d0SDaniel P. Berrange 28195e14d0SDaniel P. Berrange QIOChannelCommand * 29195e14d0SDaniel P. Berrange qio_channel_command_new_pid(int writefd, 30195e14d0SDaniel P. Berrange int readfd, 31195e14d0SDaniel P. Berrange pid_t pid) 32195e14d0SDaniel P. Berrange { 33195e14d0SDaniel P. Berrange QIOChannelCommand *ioc; 34195e14d0SDaniel P. Berrange 35195e14d0SDaniel P. Berrange ioc = QIO_CHANNEL_COMMAND(object_new(TYPE_QIO_CHANNEL_COMMAND)); 36195e14d0SDaniel P. Berrange 37195e14d0SDaniel P. Berrange ioc->readfd = readfd; 38195e14d0SDaniel P. Berrange ioc->writefd = writefd; 39195e14d0SDaniel P. Berrange ioc->pid = pid; 40195e14d0SDaniel P. Berrange 41195e14d0SDaniel P. Berrange trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid); 42195e14d0SDaniel P. Berrange return ioc; 43195e14d0SDaniel P. Berrange } 44195e14d0SDaniel P. Berrange 45195e14d0SDaniel P. Berrange 46195e14d0SDaniel P. Berrange #ifndef WIN32 47195e14d0SDaniel P. Berrange QIOChannelCommand * 48195e14d0SDaniel P. Berrange qio_channel_command_new_spawn(const char *const argv[], 49195e14d0SDaniel P. Berrange int flags, 50195e14d0SDaniel P. Berrange Error **errp) 51195e14d0SDaniel P. Berrange { 52195e14d0SDaniel P. Berrange pid_t pid = -1; 53195e14d0SDaniel P. Berrange int stdinfd[2] = { -1, -1 }; 54195e14d0SDaniel P. Berrange int stdoutfd[2] = { -1, -1 }; 55195e14d0SDaniel P. Berrange int devnull = -1; 56195e14d0SDaniel P. Berrange bool stdinnull = false, stdoutnull = false; 57195e14d0SDaniel P. Berrange QIOChannelCommand *ioc; 58195e14d0SDaniel P. Berrange 59195e14d0SDaniel P. Berrange flags = flags & O_ACCMODE; 60195e14d0SDaniel P. Berrange 61195e14d0SDaniel P. Berrange if (flags == O_RDONLY) { 62195e14d0SDaniel P. Berrange stdinnull = true; 63195e14d0SDaniel P. Berrange } 64195e14d0SDaniel P. Berrange if (flags == O_WRONLY) { 65195e14d0SDaniel P. Berrange stdoutnull = true; 66195e14d0SDaniel P. Berrange } 67195e14d0SDaniel P. Berrange 68195e14d0SDaniel P. Berrange if (stdinnull || stdoutnull) { 69195e14d0SDaniel P. Berrange devnull = open("/dev/null", O_RDWR); 70e155494cSDaniel P. Berrange if (devnull < 0) { 71195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, 72195e14d0SDaniel P. Berrange "Unable to open /dev/null"); 73195e14d0SDaniel P. Berrange goto error; 74195e14d0SDaniel P. Berrange } 75195e14d0SDaniel P. Berrange } 76195e14d0SDaniel P. Berrange 77195e14d0SDaniel P. Berrange if ((!stdinnull && pipe(stdinfd) < 0) || 78195e14d0SDaniel P. Berrange (!stdoutnull && pipe(stdoutfd) < 0)) { 79195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, 80195e14d0SDaniel P. Berrange "Unable to open pipe"); 81195e14d0SDaniel P. Berrange goto error; 82195e14d0SDaniel P. Berrange } 83195e14d0SDaniel P. Berrange 84195e14d0SDaniel P. Berrange pid = qemu_fork(errp); 85195e14d0SDaniel P. Berrange if (pid < 0) { 86195e14d0SDaniel P. Berrange goto error; 87195e14d0SDaniel P. Berrange } 88195e14d0SDaniel P. Berrange 89195e14d0SDaniel P. Berrange if (pid == 0) { /* child */ 90195e14d0SDaniel P. Berrange dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO); 91195e14d0SDaniel P. Berrange dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO); 92195e14d0SDaniel P. Berrange /* Leave stderr connected to qemu's stderr */ 93195e14d0SDaniel P. Berrange 94195e14d0SDaniel P. Berrange if (!stdinnull) { 95195e14d0SDaniel P. Berrange close(stdinfd[0]); 96195e14d0SDaniel P. Berrange close(stdinfd[1]); 97195e14d0SDaniel P. Berrange } 98195e14d0SDaniel P. Berrange if (!stdoutnull) { 99195e14d0SDaniel P. Berrange close(stdoutfd[0]); 100195e14d0SDaniel P. Berrange close(stdoutfd[1]); 101195e14d0SDaniel P. Berrange } 102e155494cSDaniel P. Berrange if (devnull != -1) { 103e155494cSDaniel P. Berrange close(devnull); 104e155494cSDaniel P. Berrange } 105195e14d0SDaniel P. Berrange 106195e14d0SDaniel P. Berrange execv(argv[0], (char * const *)argv); 107195e14d0SDaniel P. Berrange _exit(1); 108195e14d0SDaniel P. Berrange } 109195e14d0SDaniel P. Berrange 110195e14d0SDaniel P. Berrange if (!stdinnull) { 111195e14d0SDaniel P. Berrange close(stdinfd[0]); 112195e14d0SDaniel P. Berrange } 113195e14d0SDaniel P. Berrange if (!stdoutnull) { 114195e14d0SDaniel P. Berrange close(stdoutfd[1]); 115195e14d0SDaniel P. Berrange } 116195e14d0SDaniel P. Berrange 117195e14d0SDaniel P. Berrange ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1], 118195e14d0SDaniel P. Berrange stdoutnull ? devnull : stdoutfd[0], 119195e14d0SDaniel P. Berrange pid); 120195e14d0SDaniel P. Berrange trace_qio_channel_command_new_spawn(ioc, argv[0], flags); 121195e14d0SDaniel P. Berrange return ioc; 122195e14d0SDaniel P. Berrange 123195e14d0SDaniel P. Berrange error: 124e155494cSDaniel P. Berrange if (devnull != -1) { 125e155494cSDaniel P. Berrange close(devnull); 126e155494cSDaniel P. Berrange } 127195e14d0SDaniel P. Berrange if (stdinfd[0] != -1) { 128195e14d0SDaniel P. Berrange close(stdinfd[0]); 129195e14d0SDaniel P. Berrange } 130195e14d0SDaniel P. Berrange if (stdinfd[1] != -1) { 131195e14d0SDaniel P. Berrange close(stdinfd[1]); 132195e14d0SDaniel P. Berrange } 133195e14d0SDaniel P. Berrange if (stdoutfd[0] != -1) { 134195e14d0SDaniel P. Berrange close(stdoutfd[0]); 135195e14d0SDaniel P. Berrange } 136195e14d0SDaniel P. Berrange if (stdoutfd[1] != -1) { 137195e14d0SDaniel P. Berrange close(stdoutfd[1]); 138195e14d0SDaniel P. Berrange } 139195e14d0SDaniel P. Berrange return NULL; 140195e14d0SDaniel P. Berrange } 141195e14d0SDaniel P. Berrange 142195e14d0SDaniel P. Berrange #else /* WIN32 */ 143195e14d0SDaniel P. Berrange QIOChannelCommand * 144195e14d0SDaniel P. Berrange qio_channel_command_new_spawn(const char *const argv[], 145195e14d0SDaniel P. Berrange int flags, 146195e14d0SDaniel P. Berrange Error **errp) 147195e14d0SDaniel P. Berrange { 148195e14d0SDaniel P. Berrange error_setg_errno(errp, ENOSYS, 149195e14d0SDaniel P. Berrange "Command spawn not supported on this platform"); 150195e14d0SDaniel P. Berrange return NULL; 151195e14d0SDaniel P. Berrange } 152195e14d0SDaniel P. Berrange #endif /* WIN32 */ 153195e14d0SDaniel P. Berrange 154195e14d0SDaniel P. Berrange #ifndef WIN32 155195e14d0SDaniel P. Berrange static int qio_channel_command_abort(QIOChannelCommand *ioc, 156195e14d0SDaniel P. Berrange Error **errp) 157195e14d0SDaniel P. Berrange { 158195e14d0SDaniel P. Berrange pid_t ret; 159195e14d0SDaniel P. Berrange int status; 160195e14d0SDaniel P. Berrange int step = 0; 161195e14d0SDaniel P. Berrange 162195e14d0SDaniel P. Berrange /* See if intermediate process has exited; if not, try a nice 163195e14d0SDaniel P. Berrange * SIGTERM followed by a more severe SIGKILL. 164195e14d0SDaniel P. Berrange */ 165195e14d0SDaniel P. Berrange rewait: 166195e14d0SDaniel P. Berrange trace_qio_channel_command_abort(ioc, ioc->pid); 167195e14d0SDaniel P. Berrange ret = waitpid(ioc->pid, &status, WNOHANG); 168195e14d0SDaniel P. Berrange trace_qio_channel_command_wait(ioc, ioc->pid, ret, status); 169195e14d0SDaniel P. Berrange if (ret == (pid_t)-1) { 170195e14d0SDaniel P. Berrange if (errno == EINTR) { 171195e14d0SDaniel P. Berrange goto rewait; 172195e14d0SDaniel P. Berrange } else { 173195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, 174195e14d0SDaniel P. Berrange "Cannot wait on pid %llu", 175195e14d0SDaniel P. Berrange (unsigned long long)ioc->pid); 176195e14d0SDaniel P. Berrange return -1; 177195e14d0SDaniel P. Berrange } 178195e14d0SDaniel P. Berrange } else if (ret == 0) { 179195e14d0SDaniel P. Berrange if (step == 0) { 180195e14d0SDaniel P. Berrange kill(ioc->pid, SIGTERM); 181195e14d0SDaniel P. Berrange } else if (step == 1) { 182195e14d0SDaniel P. Berrange kill(ioc->pid, SIGKILL); 183195e14d0SDaniel P. Berrange } else { 184195e14d0SDaniel P. Berrange error_setg(errp, 185195e14d0SDaniel P. Berrange "Process %llu refused to die", 186195e14d0SDaniel P. Berrange (unsigned long long)ioc->pid); 187195e14d0SDaniel P. Berrange return -1; 188195e14d0SDaniel P. Berrange } 1890c0a55b2SDaniel P. Berrange step++; 190195e14d0SDaniel P. Berrange usleep(10 * 1000); 191195e14d0SDaniel P. Berrange goto rewait; 192195e14d0SDaniel P. Berrange } 193195e14d0SDaniel P. Berrange 194195e14d0SDaniel P. Berrange return 0; 195195e14d0SDaniel P. Berrange } 196195e14d0SDaniel P. Berrange #endif /* ! WIN32 */ 197195e14d0SDaniel P. Berrange 198195e14d0SDaniel P. Berrange 199195e14d0SDaniel P. Berrange static void qio_channel_command_init(Object *obj) 200195e14d0SDaniel P. Berrange { 201195e14d0SDaniel P. Berrange QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); 202195e14d0SDaniel P. Berrange ioc->readfd = -1; 203195e14d0SDaniel P. Berrange ioc->writefd = -1; 204195e14d0SDaniel P. Berrange ioc->pid = -1; 205195e14d0SDaniel P. Berrange } 206195e14d0SDaniel P. Berrange 207195e14d0SDaniel P. Berrange static void qio_channel_command_finalize(Object *obj) 208195e14d0SDaniel P. Berrange { 209195e14d0SDaniel P. Berrange QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); 210195e14d0SDaniel P. Berrange if (ioc->readfd != -1) { 211195e14d0SDaniel P. Berrange close(ioc->readfd); 212195e14d0SDaniel P. Berrange } 213e155494cSDaniel P. Berrange if (ioc->writefd != -1 && 214e155494cSDaniel P. Berrange ioc->writefd != ioc->readfd) { 215195e14d0SDaniel P. Berrange close(ioc->writefd); 216195e14d0SDaniel P. Berrange } 217e155494cSDaniel P. Berrange ioc->writefd = ioc->readfd = -1; 218195e14d0SDaniel P. Berrange if (ioc->pid > 0) { 219195e14d0SDaniel P. Berrange #ifndef WIN32 220195e14d0SDaniel P. Berrange qio_channel_command_abort(ioc, NULL); 221195e14d0SDaniel P. Berrange #endif 222195e14d0SDaniel P. Berrange } 223195e14d0SDaniel P. Berrange } 224195e14d0SDaniel P. Berrange 225195e14d0SDaniel P. Berrange 226195e14d0SDaniel P. Berrange static ssize_t qio_channel_command_readv(QIOChannel *ioc, 227195e14d0SDaniel P. Berrange const struct iovec *iov, 228195e14d0SDaniel P. Berrange size_t niov, 229195e14d0SDaniel P. Berrange int **fds, 230195e14d0SDaniel P. Berrange size_t *nfds, 231195e14d0SDaniel P. Berrange Error **errp) 232195e14d0SDaniel P. Berrange { 233195e14d0SDaniel P. Berrange QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); 234195e14d0SDaniel P. Berrange ssize_t ret; 235195e14d0SDaniel P. Berrange 236195e14d0SDaniel P. Berrange retry: 237195e14d0SDaniel P. Berrange ret = readv(cioc->readfd, iov, niov); 238195e14d0SDaniel P. Berrange if (ret < 0) { 239195e14d0SDaniel P. Berrange if (errno == EAGAIN || 240195e14d0SDaniel P. Berrange errno == EWOULDBLOCK) { 241195e14d0SDaniel P. Berrange return QIO_CHANNEL_ERR_BLOCK; 242195e14d0SDaniel P. Berrange } 243195e14d0SDaniel P. Berrange if (errno == EINTR) { 244195e14d0SDaniel P. Berrange goto retry; 245195e14d0SDaniel P. Berrange } 246195e14d0SDaniel P. Berrange 247195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, 248195e14d0SDaniel P. Berrange "Unable to read from command"); 249195e14d0SDaniel P. Berrange return -1; 250195e14d0SDaniel P. Berrange } 251195e14d0SDaniel P. Berrange 252195e14d0SDaniel P. Berrange return ret; 253195e14d0SDaniel P. Berrange } 254195e14d0SDaniel P. Berrange 255195e14d0SDaniel P. Berrange static ssize_t qio_channel_command_writev(QIOChannel *ioc, 256195e14d0SDaniel P. Berrange const struct iovec *iov, 257195e14d0SDaniel P. Berrange size_t niov, 258195e14d0SDaniel P. Berrange int *fds, 259195e14d0SDaniel P. Berrange size_t nfds, 260195e14d0SDaniel P. Berrange Error **errp) 261195e14d0SDaniel P. Berrange { 262195e14d0SDaniel P. Berrange QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); 263195e14d0SDaniel P. Berrange ssize_t ret; 264195e14d0SDaniel P. Berrange 265195e14d0SDaniel P. Berrange retry: 266195e14d0SDaniel P. Berrange ret = writev(cioc->writefd, iov, niov); 267195e14d0SDaniel P. Berrange if (ret <= 0) { 268195e14d0SDaniel P. Berrange if (errno == EAGAIN || 269195e14d0SDaniel P. Berrange errno == EWOULDBLOCK) { 270195e14d0SDaniel P. Berrange return QIO_CHANNEL_ERR_BLOCK; 271195e14d0SDaniel P. Berrange } 272195e14d0SDaniel P. Berrange if (errno == EINTR) { 273195e14d0SDaniel P. Berrange goto retry; 274195e14d0SDaniel P. Berrange } 275195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, "%s", 276195e14d0SDaniel P. Berrange "Unable to write to command"); 277195e14d0SDaniel P. Berrange return -1; 278195e14d0SDaniel P. Berrange } 279195e14d0SDaniel P. Berrange return ret; 280195e14d0SDaniel P. Berrange } 281195e14d0SDaniel P. Berrange 282195e14d0SDaniel P. Berrange static int qio_channel_command_set_blocking(QIOChannel *ioc, 283195e14d0SDaniel P. Berrange bool enabled, 284195e14d0SDaniel P. Berrange Error **errp) 285195e14d0SDaniel P. Berrange { 286195e14d0SDaniel P. Berrange QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); 287195e14d0SDaniel P. Berrange 288195e14d0SDaniel P. Berrange if (enabled) { 289195e14d0SDaniel P. Berrange qemu_set_block(cioc->writefd); 290195e14d0SDaniel P. Berrange qemu_set_block(cioc->readfd); 291195e14d0SDaniel P. Berrange } else { 292195e14d0SDaniel P. Berrange qemu_set_nonblock(cioc->writefd); 293195e14d0SDaniel P. Berrange qemu_set_nonblock(cioc->readfd); 294195e14d0SDaniel P. Berrange } 295195e14d0SDaniel P. Berrange 296195e14d0SDaniel P. Berrange return 0; 297195e14d0SDaniel P. Berrange } 298195e14d0SDaniel P. Berrange 299195e14d0SDaniel P. Berrange 300195e14d0SDaniel P. Berrange static int qio_channel_command_close(QIOChannel *ioc, 301195e14d0SDaniel P. Berrange Error **errp) 302195e14d0SDaniel P. Berrange { 303195e14d0SDaniel P. Berrange QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); 304195e14d0SDaniel P. Berrange int rv = 0; 305195e14d0SDaniel P. Berrange 306195e14d0SDaniel P. Berrange /* We close FDs before killing, because that 307195e14d0SDaniel P. Berrange * gives a better chance of clean shutdown 308195e14d0SDaniel P. Berrange */ 309e155494cSDaniel P. Berrange if (cioc->readfd != -1 && 310e155494cSDaniel P. Berrange close(cioc->readfd) < 0) { 311195e14d0SDaniel P. Berrange rv = -1; 312195e14d0SDaniel P. Berrange } 313e155494cSDaniel P. Berrange if (cioc->writefd != -1 && 314e155494cSDaniel P. Berrange cioc->writefd != cioc->readfd && 315e155494cSDaniel P. Berrange close(cioc->writefd) < 0) { 316195e14d0SDaniel P. Berrange rv = -1; 317195e14d0SDaniel P. Berrange } 318e155494cSDaniel P. Berrange cioc->writefd = cioc->readfd = -1; 319195e14d0SDaniel P. Berrange #ifndef WIN32 320195e14d0SDaniel P. Berrange if (qio_channel_command_abort(cioc, errp) < 0) { 321195e14d0SDaniel P. Berrange return -1; 322195e14d0SDaniel P. Berrange } 323195e14d0SDaniel P. Berrange #endif 324195e14d0SDaniel P. Berrange if (rv < 0) { 325195e14d0SDaniel P. Berrange error_setg_errno(errp, errno, "%s", 326195e14d0SDaniel P. Berrange "Unable to close command"); 327195e14d0SDaniel P. Berrange } 328195e14d0SDaniel P. Berrange return rv; 329195e14d0SDaniel P. Berrange } 330195e14d0SDaniel P. Berrange 331195e14d0SDaniel P. Berrange 332195e14d0SDaniel P. Berrange static GSource *qio_channel_command_create_watch(QIOChannel *ioc, 333195e14d0SDaniel P. Berrange GIOCondition condition) 334195e14d0SDaniel P. Berrange { 335195e14d0SDaniel P. Berrange QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); 336195e14d0SDaniel P. Berrange return qio_channel_create_fd_pair_watch(ioc, 337195e14d0SDaniel P. Berrange cioc->readfd, 338195e14d0SDaniel P. Berrange cioc->writefd, 339195e14d0SDaniel P. Berrange condition); 340195e14d0SDaniel P. Berrange } 341195e14d0SDaniel P. Berrange 342195e14d0SDaniel P. Berrange 343195e14d0SDaniel P. Berrange static void qio_channel_command_class_init(ObjectClass *klass, 344195e14d0SDaniel P. Berrange void *class_data G_GNUC_UNUSED) 345195e14d0SDaniel P. Berrange { 346195e14d0SDaniel P. Berrange QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); 347195e14d0SDaniel P. Berrange 348195e14d0SDaniel P. Berrange ioc_klass->io_writev = qio_channel_command_writev; 349195e14d0SDaniel P. Berrange ioc_klass->io_readv = qio_channel_command_readv; 350195e14d0SDaniel P. Berrange ioc_klass->io_set_blocking = qio_channel_command_set_blocking; 351195e14d0SDaniel P. Berrange ioc_klass->io_close = qio_channel_command_close; 352195e14d0SDaniel P. Berrange ioc_klass->io_create_watch = qio_channel_command_create_watch; 353195e14d0SDaniel P. Berrange } 354195e14d0SDaniel P. Berrange 355195e14d0SDaniel P. Berrange static const TypeInfo qio_channel_command_info = { 356195e14d0SDaniel P. Berrange .parent = TYPE_QIO_CHANNEL, 357195e14d0SDaniel P. Berrange .name = TYPE_QIO_CHANNEL_COMMAND, 358195e14d0SDaniel P. Berrange .instance_size = sizeof(QIOChannelCommand), 359195e14d0SDaniel P. Berrange .instance_init = qio_channel_command_init, 360195e14d0SDaniel P. Berrange .instance_finalize = qio_channel_command_finalize, 361195e14d0SDaniel P. Berrange .class_init = qio_channel_command_class_init, 362195e14d0SDaniel P. Berrange }; 363195e14d0SDaniel P. Berrange 364195e14d0SDaniel P. Berrange static void qio_channel_command_register_types(void) 365195e14d0SDaniel P. Berrange { 366195e14d0SDaniel P. Berrange type_register_static(&qio_channel_command_info); 367195e14d0SDaniel P. Berrange } 368195e14d0SDaniel P. Berrange 369195e14d0SDaniel P. Berrange type_init(qio_channel_command_register_types); 370