/**************************************************************************** * apps/nshlib/nsh_fsutils.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "nsh.h" #include "nsh_console.h" /**************************************************************************** * Private Types ****************************************************************************/ struct getpid_arg_s { FAR const char *name; FAR pid_t *pids; size_t count; size_t next; }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: getpid_callback * * Description: * It is a callback function of nsh_getpid * ****************************************************************************/ #if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PIDOF) static int getpid_callback(FAR struct nsh_vtbl_s *vtbl, FAR const char *dirpath, FAR struct dirent *entryp, FAR void *pvarg) { FAR struct getpid_arg_s *arg = (FAR struct getpid_arg_s *)pvarg; char buffer[PATH_MAX]; int fd; int len; if (arg->next == arg->count) { return -E2BIG; } /* Match the name of the process */ snprintf(buffer, sizeof(buffer), "%s/%s/cmdline", dirpath, entryp->d_name); fd = open(buffer, O_RDONLY | O_CLOEXEC); if (fd < 0) { return 0; } len = read(fd, buffer, sizeof(buffer) - 1); close(fd); if (len < 0) { return -errno; } buffer[len] = '\0'; len = strlen(arg->name); if (strncmp(buffer, arg->name, len) == 0 && (isspace(buffer[len]) || buffer[len] == '\0')) { arg->pids[arg->next++] = atoi(entryp->d_name); } return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: nsh_catfile * * Description: * Dump the contents of a file to the current NSH terminal. * * Input Paratemets: * vtbl - session vtbl * cmd - NSH command name to use in error reporting * filepath - The full path to the file to be dumped * * Returned Value: * Zero (OK) on success; -1 (ERROR) on failure. * ****************************************************************************/ #ifdef NSH_HAVE_CATFILE int nsh_catfile(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, FAR const char *filepath) { FAR char *buffer; int fd; int ret = OK; /* Open the file for reading */ fd = open(filepath, O_RDONLY | O_CLOEXEC); if (fd < 0) { #if defined(CONFIG_NSH_PROC_MOUNTPOINT) if (strncmp(filepath, CONFIG_NSH_PROC_MOUNTPOINT, sizeof(CONFIG_NSH_PROC_MOUNTPOINT) - 1) == 0) { nsh_error(vtbl, "nsh: %s: Could not open %s (is procfs mounted?)\n", cmd, filepath); } #endif nsh_error(vtbl, g_fmtcmdfailed, cmd, "open", NSH_ERRNO); return ERROR; } buffer = (FAR char *)malloc(IOBUFFERSIZE); if (buffer == NULL) { close(fd); nsh_error(vtbl, g_fmtcmdfailed, cmd, "malloc", NSH_ERRNO); return ERROR; } /* And just dump it byte for byte into stdout */ for (; ; ) { int nbytesread = read(fd, buffer, IOBUFFERSIZE); /* Check for read errors */ if (nbytesread < 0) { int errval = errno; /* EINTR is not an error (but will stop stop the cat) */ if (errval == EINTR) { nsh_error(vtbl, g_fmtsignalrecvd, cmd); } else { nsh_error(vtbl, g_fmtcmdfailed, cmd, "read", NSH_ERRNO_OF(errval)); } ret = ERROR; break; } /* Check for data successfully read */ else if (nbytesread > 0) { int nbyteswritten = 0; while (nbyteswritten < nbytesread) { ssize_t n = nsh_write(vtbl, buffer + nbyteswritten, nbytesread - nbyteswritten); if (n < 0) { int errcode = errno; /* EINTR is not an error (but will stop stop the cat) */ if (errcode == EINTR) { nsh_error(vtbl, g_fmtsignalrecvd, cmd); } else { nsh_error(vtbl, g_fmtcmdfailed, cmd, "write", NSH_ERRNO_OF(errcode)); } ret = ERROR; break; } else { nbyteswritten += n; } } } /* Otherwise, it is the end of file */ else { break; } } /* NOTE that the following NSH prompt may appear on the same line as file * content. The IEEE Std requires that "The standard output shall * contain the sequence of bytes read from the input files. Nothing else * shall be written to the standard output." Reference: * https://pubs.opengroup.org/onlinepubs/009695399/utilities/cat.html. */ /* Close the input file and return the result */ close(fd); free(buffer); return ret; } #endif /**************************************************************************** * Name: nsh_readfile * * Description: * Read a small file into a user-provided buffer. The data is assumed to * be a string and is guaranteed to be NUL-terminated. An error occurs if * the file content (+terminator) will not fit into the provided 'buffer'. * * Input Parameters: * vtbl - The console vtable * filepath - The full path to the file to be read * buffer - The user-provided buffer into which the file is read. * buflen - The size of the user provided buffer * * Returned Value: * Zero (OK) is returned on success; -1 (ERROR) is returned on any * failure to read the fil into the buffer. * ****************************************************************************/ #ifdef NSH_HAVE_READFILE int nsh_readfile(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, FAR const char *filepath, FAR char *buffer, size_t buflen) { FAR char *bufptr; size_t remaining; ssize_t nread; ssize_t ntotal; int fd; int ret; /* Open the file */ fd = open(filepath, O_RDONLY | O_CLOEXEC); if (fd < 0) { nsh_error(vtbl, g_fmtcmdfailed, cmd, "open", NSH_ERRNO); return ERROR; } /* Read until we hit the end of the file, until we have exhausted the * buffer space, or until some irrecoverable error occurs */ ntotal = 0; /* No bytes read yet */ *buffer = '\0'; /* NUL terminate the empty buffer */ bufptr = buffer; /* Working pointer */ remaining = buflen - 1; /* Reserve one byte for a NUL terminator */ ret = ERROR; /* Assume failure */ do { nread = read(fd, bufptr, remaining); if (nread < 0) { /* Read error */ int errcode = errno; DEBUGASSERT(errcode > 0); /* EINTR is not a read error. It simply means that a signal was * received while waiting for the read to complete. */ if (errcode != EINTR) { /* Fatal error */ nsh_error(vtbl, g_fmtcmdfailed, cmd, "read", NSH_ERRNO); break; } } else if (nread == 0) { /* End of file */ ret = OK; break; } else { /* Successful read. Make sure that the buffer is null terminated */ DEBUGASSERT(nread <= (ssize_t)remaining); ntotal += nread; buffer[ntotal] = '\0'; /* Bump up the read count and continuing reading to the end of * file. */ bufptr += nread; remaining -= nread; } } while (buflen > 0); /* Close the file and return. */ close(fd); return ret; } #endif /**************************************************************************** * Name: nsh_writefile * * Description: * Dump the contents of a file to the current NSH terminal. * * Input Paratemets: * vtbl - session vtbl * cmd - NSH command name to use in error reporting * buffer - The pointer of writing buffer * len - The length of writing buffer * filepath - The full path to the file to be dumped * * Returned Value: * Zero (OK) on success; -1 (ERROR) on failure. * ****************************************************************************/ #ifdef NSH_HAVE_WRITEFILE int nsh_writefile(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, FAR const char *buffer, size_t len, FAR const char *filepath) { int fd; int ret; /* Open the file for reading */ fd = open(filepath, O_WRONLY | O_CLOEXEC); if (fd < 0) { #if defined(CONFIG_NSH_PROC_MOUNTPOINT) if (strncmp(filepath, CONFIG_NSH_PROC_MOUNTPOINT, sizeof(CONFIG_NSH_PROC_MOUNTPOINT) - 1) == 0) { nsh_error(vtbl, "nsh: %s: Could not open %s (is procfs mounted?)\n", cmd, filepath); } #endif nsh_error(vtbl, g_fmtcmdfailed, cmd, "open", NSH_ERRNO); return ERROR; } ret = write(fd, buffer, len); if (ret < 0) { nsh_error(vtbl, g_fmtcmdfailed, cmd, "write", NSH_ERRNO); } close(fd); return ret > 0 ? OK : ERROR; } #endif /**************************************************************************** * Name: nsh_foreach_direntry * * Description: * Call the provided 'handler' for each entry found in the directory at * 'dirpath'. * * Input Parameters * vtbl - The console vtable * cmd - NSH command name to use in error reporting * dirpath - The full path to the directory to be traversed * handler - The handler to be called for each entry of the directory * pvarg - User provided argument to be passed to the 'handler' * * Returned Value: * Zero (OK) returned on success; -1 (ERROR) returned on failure. * ****************************************************************************/ #ifdef NSH_HAVE_FOREACH_DIRENTRY int nsh_foreach_direntry(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, FAR const char *dirpath, nsh_direntry_handler_t handler, void *pvarg) { DIR *dirp; int ret = OK; /* Open the directory */ dirp = opendir(dirpath); if (dirp == NULL) { /* Failed to open the directory */ #if defined(CONFIG_NSH_PROC_MOUNTPOINT) if (strncmp(dirpath, CONFIG_NSH_PROC_MOUNTPOINT, sizeof(CONFIG_NSH_PROC_MOUNTPOINT) - 1) == 0) { nsh_error(vtbl, "nsh: %s: Could not open %s (is procfs mounted?)\n", cmd, dirpath); } #endif nsh_error(vtbl, g_fmtcmdfailed, cmd, "opendir", NSH_ERRNO); return ERROR; } /* Read each directory entry */ for (; ; ) { FAR struct dirent *entryp = readdir(dirp); if (entryp == NULL) { /* Finished with this directory */ break; } /* Call the handler with this directory entry */ if (handler(vtbl, dirpath, entryp, pvarg) < 0) { /* The handler reported a problem */ ret = ERROR; break; } } closedir(dirp); return ret; } #endif /**************************************************************************** * Name: nsh_trimdir * * Description: * Skip any trailing '/' characters (unless it is also the leading '/') * * Input Parameters: * dirpath - The directory path to be trimmed. May be modified! * * Returned value: * None * ****************************************************************************/ #ifdef NSH_HAVE_TRIMDIR void nsh_trimdir(FAR char *dirpath) { /* Skip any trailing '/' characters (unless it is also the leading '/') */ int len = strlen(dirpath) - 1; while (len > 0 && dirpath[len] == '/') { dirpath[len] = '\0'; len--; } } #endif /**************************************************************************** * Name: nsh_trimspaces * * Description: * Trim any leading or trailing spaces from a string. * * Input Parameters: * str - The string to be trimmed. May be modified! * * Returned value: * The new string pointer. * ****************************************************************************/ #ifdef NSH_HAVE_TRIMSPACES FAR char *nsh_trimspaces(FAR char *str) { FAR char *trimmed; int ndx; /* Strip leading whitespace from the value */ for (trimmed = str; *trimmed != '\0' && isspace(*trimmed); trimmed++); /* Strip trailing whitespace from the value */ for (ndx = strlen(trimmed) - 1; ndx >= 0 && isspace(trimmed[ndx]); ndx--) { trimmed[ndx] = '\0'; } return trimmed; } #endif /**************************************************************************** * Name: nsh_getdirpath * * Description: * Combine dirpath with a file/path, this will generated a new string, * which need free outside. * * Input Parameters: * dirpath - the dirpath * path - the file/path * * Returned value: * The new string pointer, need free in caller. * ****************************************************************************/ #ifdef NSH_HAVE_IOBUFFER FAR char *nsh_getdirpath(FAR struct nsh_vtbl_s *vtbl, FAR const char *dirpath, FAR const char *path) { /* Handle the case where all that is left is '/' */ if (strcmp(dirpath, "/") == 0) { snprintf(vtbl->iobuffer, IOBUFFERSIZE, "/%s", path); } else { snprintf(vtbl->iobuffer, IOBUFFERSIZE, "%s/%s", dirpath, path); } return strdup(vtbl->iobuffer); } #endif /**************************************************************************** * Name: nsh_getpid * * Description: * Obtain pid through process name * * Input Parameters: * vtbl - NSH session data * name - the name of the process * pids - allocated array for storing pid * count - the maximum number of pids obtained * * Returned value: * the actual number of pids obtained * ****************************************************************************/ #if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PIDOF) ssize_t nsh_getpid(FAR struct nsh_vtbl_s *vtbl, FAR const char *name, FAR pid_t *pids, size_t count) { struct getpid_arg_s argv = { name, pids, count, 0 }; if (NULL == name || pids == NULL) { return -EINVAL; } /* No need to determine the return value */ nsh_foreach_direntry(vtbl, "pidof", CONFIG_NSH_PROC_MOUNTPOINT, getpid_callback, &argv); return argv.next; } #endif