diff --git a/contrib/openbsm/compat/flopen.h b/contrib/openbsm/compat/flopen.h index 7871b13c183f..be9114ab4f44 100644 --- a/contrib/openbsm/compat/flopen.h +++ b/contrib/openbsm/compat/flopen.h @@ -1,102 +1,102 @@ /*- - * Copyright (c) 2007 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2007 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Derived from FreeBSD head/lib/libutil/flopen.c r193591 */ #include #include #include #include #include static int flopen(const char *path, int flags, ...) { int fd, operation, serrno, trunc; struct stat sb, fsb; mode_t mode; #ifdef O_EXLOCK flags &= ~O_EXLOCK; #endif mode = 0; if (flags & O_CREAT) { va_list ap; va_start(ap, flags); mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */ va_end(ap); } operation = LOCK_EX; if (flags & O_NONBLOCK) operation |= LOCK_NB; trunc = (flags & O_TRUNC); flags &= ~O_TRUNC; for (;;) { if ((fd = open(path, flags, mode)) == -1) /* non-existent or no access */ return (-1); if (flock(fd, operation) == -1) { /* unsupported or interrupted */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } if (stat(path, &sb) == -1) { /* disappeared from under our feet */ (void)close(fd); continue; } if (fstat(fd, &fsb) == -1) { /* can't happen [tm] */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) { /* changed under our feet */ (void)close(fd); continue; } if (trunc && ftruncate(fd, 0) != 0) { /* can't happen [tm] */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } return (fd); } } diff --git a/lib/libutil/flopen.3 b/lib/libutil/flopen.3 index 61c26231cc59..259bbe16e9f7 100644 --- a/lib/libutil/flopen.3 +++ b/lib/libutil/flopen.3 @@ -1,125 +1,125 @@ .\"- -.\" Copyright (c) 2007 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2007 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd July 28, 2017 .Dt FLOPEN 3 .Os .Sh NAME .Nm flopen , .Nm flopenat .Nd "Reliably open and lock a file" .Sh LIBRARY .Lb libutil .Sh SYNOPSIS .In sys/fcntl.h .In libutil.h .Ft int .Fn flopen "const char *path" "int flags" .Ft int .Fn flopen "const char *path" "int flags" "mode_t mode" .Ft int .Fn flopenat "int fd" "const char *path" "int flags" .Ft int .Fn flopenat "int fd" "const char *path" "int flags" "mode_t mode" .Sh DESCRIPTION The .Fn flopen function opens or creates a file and acquires an exclusive lock on it. It is essentially equivalent with calling .Fn open with the same parameters followed by .Fn flock with an .Fa operation argument of .Dv LOCK_EX , except that .Fn flopen will attempt to detect and handle races that may occur between opening / creating the file and locking it. Thus, it is well suited for opening lock files, PID files, spool files, mailboxes and other kinds of files which are used for synchronization between processes. .Pp If .Fa flags includes .Dv O_NONBLOCK and the file is already locked, .Fn flopen will fail and set .Va errno to .Dv EWOULDBLOCK . .Pp As with .Fn open , the additional .Fa mode argument is required if .Fa flags includes .Dv O_CREAT . .Pp The .Fn flopenat function is equivalent to the .Fn flopen function except in the case where the .Fa path specifies a relative path. In this case the file to be opened is determined relative to the directory associated with the file descriptor .Fa fd instead of the current working directory. If .Fn flopenat is passed the special value .Dv AT_FDCWD in the .Fa fd parameter, the current working directory is used and the behavior is identical to a call to .Fn flopen . .Sh RETURN VALUES If successful, .Fn flopen returns a valid file descriptor. Otherwise, it returns -1, and sets .Va errno as described in .Xr flock 2 and .Xr open 2 . .Sh SEE ALSO .Xr errno 2 , .Xr flock 2 , .Xr open 2 .Sh AUTHORS .An -nosplit The .Nm function and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/lib/libutil/flopen.c b/lib/libutil/flopen.c index b1961894c11f..9fb3ec8aeae0 100644 --- a/lib/libutil/flopen.c +++ b/lib/libutil/flopen.c @@ -1,145 +1,145 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2007-2009 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include /* * Reliably open and lock a file. * * Please do not modify this code without first reading the revision history * and discussing your changes with . Don't be fooled by the * code's apparent simplicity; there would be no need for this function if it * was easy to get right. */ static int vflopenat(int dirfd, const char *path, int flags, va_list ap) { int fd, operation, serrno, trunc; struct stat sb, fsb; mode_t mode; #ifdef O_EXLOCK flags &= ~O_EXLOCK; #endif mode = 0; if (flags & O_CREAT) { mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */ } operation = LOCK_EX; if (flags & O_NONBLOCK) operation |= LOCK_NB; trunc = (flags & O_TRUNC); flags &= ~O_TRUNC; for (;;) { if ((fd = openat(dirfd, path, flags, mode)) == -1) /* non-existent or no access */ return (-1); if (flock(fd, operation) == -1) { /* unsupported or interrupted */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } if (fstatat(dirfd, path, &sb, 0) == -1) { /* disappeared from under our feet */ (void)close(fd); continue; } if (fstat(fd, &fsb) == -1) { /* can't happen [tm] */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) { /* changed under our feet */ (void)close(fd); continue; } if (trunc && ftruncate(fd, 0) != 0) { /* can't happen [tm] */ serrno = errno; (void)close(fd); errno = serrno; return (-1); } /* * The following change is provided as a specific example to * avoid. */ #if 0 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { serrno = errno; (void)close(fd); errno = serrno; return (-1); } #endif return (fd); } } int flopen(const char *path, int flags, ...) { va_list ap; int ret; va_start(ap, flags); ret = vflopenat(AT_FDCWD, path, flags, ap); va_end(ap); return (ret); } int flopenat(int dirfd, const char *path, int flags, ...) { va_list ap; int ret; va_start(ap, flags); ret = vflopenat(dirfd, path, flags, ap); va_end(ap); return (ret); } diff --git a/lib/libutil/kld.3 b/lib/libutil/kld.3 index 0f751e5ee194..6bcf62508aea 100644 --- a/lib/libutil/kld.3 +++ b/lib/libutil/kld.3 @@ -1,91 +1,91 @@ .\"- -.\" Copyright (c) 2006 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2006 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd February 18, 2006 .Dt KLD 3 .Os .Sh NAME .Nm kld_isloaded , .Nm kld_load .Nd kld utility functions .Sh LIBRARY .Lb libutil .Sh SYNOPSIS .In libutil.h .Ft int .Fn kld_isloaded "const char *name" .Ft int .Fn kld_load "const char *name" .Sh DESCRIPTION These functions facilitate loading kernel modules from userland applications. .Pp The .Fn kld_isloaded function takes a name and returns a non-zero value if a module of that name is currently loaded. The name can be either the name of a module file .Pq Pa cpufreq.ko , the same name without the .Pa .ko extension .Pq Pa cpufreq , or the name of a module contained within that file .Pq Pa cpu/ichss . Only the latter will return correct results if the module is compiled into the kernel. .Pp The .Fn kld_load function is a simple wrapper around the .Xr kldload 2 function. It returns zero if and only if the corresponding .Fn kldload call succeeded or returned .Er EEXIST (signifying that the requested module was already loaded). .Sh SEE ALSO .Xr kldfirstmod 2 , .Xr kldload 2 , .Xr kldnext 2 , .Xr kldstat 2 , .Xr modfnext 2 , .Xr modstat 2 , .Xr kld 4 .Sh HISTORY The .Fn kld_isloaded and .Fn kld_load functions first appeared in .Fx 6.3 . .Sh AUTHORS The .Fn kld_isloaded and .Fn kld_load functions and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/lib/libutil/kld.c b/lib/libutil/kld.c index 2a79c9302f0f..374a1992bc55 100644 --- a/lib/libutil/kld.c +++ b/lib/libutil/kld.c @@ -1,76 +1,76 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2006 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2006 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include int kld_isloaded(const char *name) { struct kld_file_stat fstat; struct module_stat mstat; const char *ko; int fid, mid; for (fid = kldnext(0); fid > 0; fid = kldnext(fid)) { fstat.version = sizeof(fstat); if (kldstat(fid, &fstat) != 0) continue; /* check if the file name matches the supplied name */ if (strcmp(fstat.name, name) == 0) return (1); /* strip .ko and try again */ if ((ko = strstr(fstat.name, ".ko")) != NULL && strlen(name) == (size_t)(ko - fstat.name) && strncmp(fstat.name, name, ko - fstat.name) == 0) return (1); /* look for a matching module within the file */ for (mid = kldfirstmod(fid); mid > 0; mid = modfnext(mid)) { mstat.version = sizeof(mstat); if (modstat(mid, &mstat) != 0) continue; if (strcmp(mstat.name, name) == 0) return (1); } } return (0); } int kld_load(const char *name) { if (kldload(name) == -1 && errno != EEXIST) return (-1); return (0); } diff --git a/lib/libutil/quotafile.3 b/lib/libutil/quotafile.3 index 9654d14cf669..5d2bff56baba 100644 --- a/lib/libutil/quotafile.3 +++ b/lib/libutil/quotafile.3 @@ -1,288 +1,288 @@ .\"- -.\" Copyright (c) 2009 Dag-Erling Coïdan Smørgrav and +.\" Copyright (c) 2009 Dag-Erling Smørgrav and .\" Marshall Kirk McKusick. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd December 28, 2009 .Dt QUOTAFILE 3 .Os .Sh NAME .Nm quota_open , .Nm quota_close , .Nm quota_on , .Nm quota_off , .Nm quota_read , .Nm quota_write_limits , .Nm quota_write_usage , .Nm quota_fsname , .Nm quota_qfname , .Nm quota_maxid , .Nm quota_check_path , .Nm quota_convert .Nd "Manipulate quotas" .Sh LIBRARY .Lb libutil .Sh SYNOPSIS .In sys/param.h .In sys/mount.h .In ufs/ufs/quota.h .In fcntl.h .In fstab.h .In libutil.h .Ft "struct quotafile *" .Fn quota_open "struct fstab *fs" "int quotatype" "int openflags" .Ft int .Fn quota_close "struct quotafile *qf" .Ft int .Fn quota_on "const struct quotafile *qf" .Ft int .Fn quota_off "const struct quotafile *qf" .Ft int .Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int id" .Ft int .Fn quota_write_limits "struct quotafile *qf" "struct dqblk *dqb" "int id" .Ft int .Fn quota_write_usage "struct quotafile *qf" "struct dqblk *dqb" "int id" .Ft "const char *" .Fn quota_fsname "const struct quotafile *qf" .Ft "const char *" .Fn quota_qfname "const struct quotafile *qf" .Ft int .Fn quota_maxid "const struct quotafile *qf" .Ft int .Fn quota_check_path "const struct quotafile *qf" "const char *path" .Ft int .Fn quota_convert "struct quotafile *qf" "int wordsize" .Sh DESCRIPTION These functions are designed to simplify access to filesystem quotas. If quotas are active on a filesystem, these functions will access them directly from the kernel using the .Fn quotactl system call. If quotas are not active, these functions will access them by reading and writing the quota files directly. .Pp The .Fn quota_open function takes a pointer to an .Vt fstab entry corresponding to the filesystem on which quotas are to be accessed. The .Va quotatype field indicates the type of quotas being sought, either .Dv USRQUOTA or .Dv GRPQUOTA . The .Va openflags are those used by the .Fn open system call, usually either .Dv O_RDONLY if the quotas are just to be read, or .Dv O_RDWR if the quotas are to be updated. The .Dv O_CREAT flag should be specified if a new quota file of the requested type should be created if it does not already exist. .Pp The .Fn quota_close function closes any open file descriptors and frees any storage associated with the filesystem and quota type referenced by .Va qf . .Pp The .Fn quota_on function enables quotas for the filesystem associated with its .Va qf argument which may have been opened with .Dv O_RDONLY or .Dv O_RDWR . The .Fn quota_on function returns 0 if successful; otherwise the value\~-1 is returned and the global variable .Va errno is set to indicate the error, see .Xr quotactl 2 for the possible errors. .Pp The .Fn quota_off function disables quotas for the filesystem associated with its .Va qf argument which may have been opened with .Dv O_RDONLY or .Dv O_RDWR . The .Fn quota_off function returns 0 if successful; otherwise the value\~-1 is returned and the global variable .Va errno is set to indicate the error, see .Xr quotactl 2 for the possible errors. .Pp The .Fn quota_read function reads the quota from the filesystem and quota type referenced by .Va qf for the user (or group) specified by .Va id into the .Vt dqblk quota structure pointed to by .Va dqb . .Pp The .Fn quota_write_limits function updates the limit fields (but not the usage fields) for the filesystem and quota type referenced by .Va qf for the user (or group) specified by .Va id from the .Vt dqblk quota structure pointed to by .Va dqb . .Pp The .Fn quota_write_usage function updates the usage fields (but not the limit fields) for the filesystem and quota type referenced by .Va qf for the user (or group) specified by .Va id from the .Vt dqblk quota structure pointed to by .Va dqb . .Pp The .Fn quota_fsname function returns a pointer to a buffer containing the path to the root of the file system that corresponds to its .Va qf argument, as listed in .Pa /etc/fstab . Note that this may be a symbolic link to the actual directory. .Pp The .Fn quota_qfname function returns a pointer to a buffer containing the name of the quota file that corresponds to its .Va qf argument. Note that this may be a symbolic link to the actual file. .Pp The .Fn quota_maxid function returns the maximum user (or group) .Va id contained in the quota file associated with its .Va qf argument. .Pp The .Fn quota_check_path function checks if the specified path is within the filesystem that corresponds to its .Va qf argument. If the .Va path argument refers to a symbolic link, .Fn quota_check_path will follow it. .Pp The .Fn quota_convert function converts the quota file associated with its .Va qf argument to the data size specified by its .Va wordsize argument. The supported wordsize arguments are 32 for the old 32-bit quota file format and 64 for the new 64-bit quota file format. The .Fn quota_convert function may only be called to operate on quota files that are not currently active. .Sh IMPLEMENTATION NOTES If the underlying quota file is in or converted to the old 32-bit format, limit and usage values written to the quota file will be clipped to 32 bits. .Sh RETURN VALUES If the filesystem has quotas associated with it, .Fn quota_open returns a pointer to a .Vt quotafile structure used in subsequent quota access calls. If the filesystem has no quotas, or access permission is denied .Dv NULL is returned and .Va errno is set to indicate the error. .Pp The .Fn quota_check_path function returns\~1 for a positive result and\~0 for a negative result. If an error occurs, it returns\~-1 and sets .Va errno to indicate the error. .Pp The .Fn quota_read , .Fn quota_write_limits , .Fn quota_write_usage , .Fn quota_convert , and .Fn quota_close functions return zero on success. On error they return\~-1 and set .Va errno to indicate the error. .Sh SEE ALSO .Xr quotactl 2 , .Xr quota.group 5 , .Xr quota.user 5 .Sh HISTORY The .Nm quotafile functions first appeared in .Fx 8.1 . .Sh AUTHORS .An -nosplit The .Nm quotafile functions and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org and .An Marshall Kirk McKusick Aq Mt mckusick@mckusick.com . diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c index 047a2a0e6c2f..1f02813dee92 100644 --- a/lib/libutil/quotafile.c +++ b/lib/libutil/quotafile.c @@ -1,605 +1,605 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008 Dag-Erling Smørgrav * Copyright (c) 2008 Marshall Kirk McKusick * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct quotafile { int fd; /* -1 means using quotactl for access */ int accmode; /* access mode */ int wordsize; /* 32-bit or 64-bit limits */ int quotatype; /* USRQUOTA or GRPQUOTA */ dev_t dev; /* device */ char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */ char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */ }; static const char *qfextension[] = INITQFNAMES; /* * Check to see if a particular quota is to be enabled. */ static int hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) { char *opt; char *cp; struct statfs sfb; char buf[BUFSIZ]; static char initname, usrname[100], grpname[100]; /* * 1) we only need one of these * 2) fstab may specify a different filename */ if (!initname) { (void)snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], QUOTAFILENAME); (void)snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], QUOTAFILENAME); initname = 1; } strcpy(buf, fs->fs_mntops); for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { if ((cp = strchr(opt, '='))) *cp++ = '\0'; if (type == USRQUOTA && strcmp(opt, usrname) == 0) break; if (type == GRPQUOTA && strcmp(opt, grpname) == 0) break; } if (!opt) return (0); /* * Ensure that the filesystem is mounted. */ if (statfs(fs->fs_file, &sfb) != 0 || strcmp(fs->fs_file, sfb.f_mntonname)) { return (0); } if (cp) { strlcpy(qfnamep, cp, qfbufsize); } else { (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[type]); } return (1); } struct quotafile * quota_open(struct fstab *fs, int quotatype, int openflags) { struct quotafile *qf; struct dqhdr64 dqh; struct group *grp; struct stat st; int qcmd, serrno = 0; int ufs; if ((qf = calloc(1, sizeof(*qf))) == NULL) return (NULL); qf->fd = -1; qf->quotatype = quotatype; strlcpy(qf->fsname, fs->fs_file, sizeof(qf->fsname)); if (stat(qf->fsname, &st) != 0) goto error; qf->dev = st.st_dev; qcmd = QCMD(Q_GETQUOTASIZE, quotatype); ufs = strcmp(fs->fs_vfstype, "ufs") == 0; /* * On UFS, hasquota() fills in qf->qfname. But we only care about * this for UFS. So we need to call hasquota() for UFS, first. */ if (ufs) { serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); } if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) return (qf); if (!ufs) { errno = 0; goto error; } else if (serrno == 0) { errno = EOPNOTSUPP; goto error; } qf->accmode = openflags & O_ACCMODE; if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 && (openflags & O_CREAT) != O_CREAT) goto error; /* File open worked, so process it */ if (qf->fd != -1) { qf->wordsize = 32; switch (read(qf->fd, &dqh, sizeof(dqh))) { case -1: goto error; case sizeof(dqh): if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { /* no magic, assume 32 bits */ qf->wordsize = 32; return (qf); } if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { /* correct magic, wrong version / lengths */ errno = EINVAL; goto error; } qf->wordsize = 64; return (qf); default: qf->wordsize = 32; return (qf); } /* not reached */ } /* open failed, but O_CREAT was specified, so create a new file */ if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) < 0) goto error; qf->wordsize = 64; memset(&dqh, 0, sizeof(dqh)); memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { /* it was one we created ourselves */ unlink(qf->qfname); goto error; } grp = getgrnam(QUOTAGROUP); fchown(qf->fd, 0, grp ? grp->gr_gid : 0); fchmod(qf->fd, 0640); return (qf); error: serrno = errno; /* did we have an open file? */ if (qf->fd != -1) close(qf->fd); free(qf); errno = serrno; return (NULL); } void quota_close(struct quotafile *qf) { if (qf->fd != -1) close(qf->fd); free(qf); } int quota_on(struct quotafile *qf) { int qcmd; qcmd = QCMD(Q_QUOTAON, qf->quotatype); return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); } int quota_off(struct quotafile *qf) { return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); } const char * quota_fsname(const struct quotafile *qf) { return (qf->fsname); } const char * quota_qfname(const struct quotafile *qf) { return (qf->qfname); } int quota_check_path(const struct quotafile *qf, const char *path) { struct stat st; if (stat(path, &st) == -1) return (-1); return (st.st_dev == qf->dev); } int quota_maxid(struct quotafile *qf) { struct stat st; int maxid; if (stat(qf->qfname, &st) < 0) return (0); switch (qf->wordsize) { case 32: maxid = st.st_size / sizeof(struct dqblk32) - 1; break; case 64: maxid = st.st_size / sizeof(struct dqblk64) - 2; break; default: maxid = 0; break; } return (maxid > 0 ? maxid : 0); } static int quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) { struct dqblk32 dqb32; off_t off; off = id * sizeof(struct dqblk32); if (lseek(qf->fd, off, SEEK_SET) == -1) return (-1); switch (read(qf->fd, &dqb32, sizeof(dqb32))) { case 0: memset(dqb, 0, sizeof(*dqb)); return (0); case sizeof(dqb32): dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; dqb->dqb_curblocks = dqb32.dqb_curblocks; dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; dqb->dqb_curinodes = dqb32.dqb_curinodes; dqb->dqb_btime = dqb32.dqb_btime; dqb->dqb_itime = dqb32.dqb_itime; return (0); default: return (-1); } } static int quota_read64(struct quotafile *qf, struct dqblk *dqb, int id) { struct dqblk64 dqb64; off_t off; off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); if (lseek(qf->fd, off, SEEK_SET) == -1) return (-1); switch (read(qf->fd, &dqb64, sizeof(dqb64))) { case 0: memset(dqb, 0, sizeof(*dqb)); return (0); case sizeof(dqb64): dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); dqb->dqb_btime = be64toh(dqb64.dqb_btime); dqb->dqb_itime = be64toh(dqb64.dqb_itime); return (0); default: return (-1); } } int quota_read(struct quotafile *qf, struct dqblk *dqb, int id) { int qcmd; if (qf->fd == -1) { qcmd = QCMD(Q_GETQUOTA, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb)); } switch (qf->wordsize) { case 32: return (quota_read32(qf, dqb, id)); case 64: return (quota_read64(qf, dqb, id)); default: errno = EINVAL; return (-1); } /* not reached */ } #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) static int quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) { struct dqblk32 dqb32; off_t off; dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); dqb32.dqb_btime = CLIP32(dqb->dqb_btime); dqb32.dqb_itime = CLIP32(dqb->dqb_itime); off = id * sizeof(struct dqblk32); if (lseek(qf->fd, off, SEEK_SET) == -1) return (-1); if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) return (0); return (-1); } static int quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) { struct dqblk64 dqb64; off_t off; dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); dqb64.dqb_btime = htobe64(dqb->dqb_btime); dqb64.dqb_itime = htobe64(dqb->dqb_itime); off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); if (lseek(qf->fd, off, SEEK_SET) == -1) return (-1); if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) return (0); return (-1); } int quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) { struct dqblk dqbuf; int qcmd; if (qf->fd == -1) { qcmd = QCMD(Q_SETUSE, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb)); } /* * Have to do read-modify-write of quota in file. */ if ((qf->accmode & O_RDWR) != O_RDWR) { errno = EBADF; return (-1); } if (quota_read(qf, &dqbuf, id) != 0) return (-1); /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. */ if (dqbuf.dqb_bsoftlimit && id != 0 && dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) dqbuf.dqb_btime = 0; if (dqbuf.dqb_isoftlimit && id != 0 && dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) dqbuf.dqb_itime = 0; dqbuf.dqb_curinodes = dqb->dqb_curinodes; dqbuf.dqb_curblocks = dqb->dqb_curblocks; /* * Write it back. */ switch (qf->wordsize) { case 32: return (quota_write32(qf, &dqbuf, id)); case 64: return (quota_write64(qf, &dqbuf, id)); default: errno = EINVAL; return (-1); } /* not reached */ } int quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) { struct dqblk dqbuf; int qcmd; if (qf->fd == -1) { qcmd = QCMD(Q_SETQUOTA, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb)); } /* * Have to do read-modify-write of quota in file. */ if ((qf->accmode & O_RDWR) != O_RDWR) { errno = EBADF; return (-1); } if (quota_read(qf, &dqbuf, id) != 0) return (-1); /* * Reset time limit if have a soft limit and were * previously under it, but are now over it * or if there previously was no soft limit, but * now have one and are over it. */ if (dqbuf.dqb_bsoftlimit && id != 0 && dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) dqb->dqb_btime = 0; if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && dqb->dqb_bsoftlimit > 0 && dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) dqb->dqb_btime = 0; if (dqbuf.dqb_isoftlimit && id != 0 && dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) dqb->dqb_itime = 0; if (dqbuf.dqb_isoftlimit == 0 && id !=0 && dqb->dqb_isoftlimit > 0 && dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) dqb->dqb_itime = 0; dqb->dqb_curinodes = dqbuf.dqb_curinodes; dqb->dqb_curblocks = dqbuf.dqb_curblocks; /* * Write it back. */ switch (qf->wordsize) { case 32: return (quota_write32(qf, dqb, id)); case 64: return (quota_write64(qf, dqb, id)); default: errno = EINVAL; return (-1); } /* not reached */ } /* * Convert a quota file from one format to another. */ int quota_convert(struct quotafile *qf, int wordsize) { struct quotafile *newqf; struct dqhdr64 dqh; struct dqblk dqblk; struct group *grp; int serrno, maxid, id, fd; /* * Quotas must not be active and quotafile must be open * for reading and writing. */ if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { errno = EBADF; return (-1); } if ((wordsize != 32 && wordsize != 64) || wordsize == qf->wordsize) { errno = EINVAL; return (-1); } maxid = quota_maxid(qf); if ((newqf = calloc(1, sizeof(*qf))) == NULL) { errno = ENOMEM; return (-1); } *newqf = *qf; snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, qf->wordsize); if (rename(qf->qfname, newqf->qfname) < 0) { free(newqf); return (-1); } if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) < 0) { serrno = errno; goto error; } newqf->wordsize = wordsize; if (wordsize == 64) { memset(&dqh, 0, sizeof(dqh)); memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { serrno = errno; goto error; } } grp = getgrnam(QUOTAGROUP); fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); fchmod(newqf->fd, 0640); for (id = 0; id <= maxid; id++) { if ((quota_read(qf, &dqblk, id)) < 0) break; switch (newqf->wordsize) { case 32: if ((quota_write32(newqf, &dqblk, id)) < 0) break; continue; case 64: if ((quota_write64(newqf, &dqblk, id)) < 0) break; continue; default: errno = EINVAL; break; } } if (id < maxid) { serrno = errno; goto error; } /* * Update the passed in quotafile to reference the new file * of the converted format size. */ fd = qf->fd; qf->fd = newqf->fd; newqf->fd = fd; qf->wordsize = newqf->wordsize; quota_close(newqf); return (0); error: /* put back the original file */ (void) rename(newqf->qfname, qf->qfname); quota_close(newqf); errno = serrno; return (-1); } diff --git a/lib/libutil/tests/flopen_test.c b/lib/libutil/tests/flopen_test.c index b86dd123eb9b..94129ebfac46 100644 --- a/lib/libutil/tests/flopen_test.c +++ b/lib/libutil/tests/flopen_test.c @@ -1,208 +1,208 @@ /*- - * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2007-2009 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include /* * Test that flopen() can create a file. */ const char * test_flopen_create(void) { const char *fn = "test_flopen_create"; const char *result = NULL; int fd; unlink(fn); fd = flopen(fn, O_RDWR|O_CREAT, 0640); if (fd < 0) { result = strerror(errno); } else { close(fd); } unlink(fn); return (result); } /* * Test that flopen() can open an existing file. */ const char * test_flopen_open(void) { const char *fn = "test_flopen_open"; const char *result = NULL; int fd; fd = open(fn, O_RDWR|O_CREAT, 0640); if (fd < 0) { result = strerror(errno); } else { close(fd); fd = flopen(fn, O_RDWR); if (fd < 0) { result = strerror(errno); } else { close(fd); } } unlink(fn); return (result); } /* * Test that flopen() can lock against itself */ const char * test_flopen_lock_self(void) { const char *fn = "test_flopen_lock_self"; const char *result = NULL; int fd1, fd2; unlink(fn); fd1 = flopen(fn, O_RDWR|O_CREAT, 0640); if (fd1 < 0) { result = strerror(errno); } else { fd2 = flopen(fn, O_RDWR|O_NONBLOCK); if (fd2 >= 0) { result = "second open succeeded"; close(fd2); } close(fd1); } unlink(fn); return (result); } /* * Test that flopen() can lock against other processes */ const char * test_flopen_lock_other(void) { const char *fn = "test_flopen_lock_other"; const char *result = NULL; volatile int fd1, fd2; unlink(fn); fd1 = flopen(fn, O_RDWR|O_CREAT, 0640); if (fd1 < 0) { result = strerror(errno); } else { fd2 = -42; if (vfork() == 0) { fd2 = flopen(fn, O_RDWR|O_NONBLOCK); close(fd2); _exit(0); } if (fd2 == -42) result = "vfork() doesn't work as expected"; if (fd2 >= 0) result = "second open succeeded"; close(fd1); } unlink(fn); return (result); } /* * Test that child processes inherit the lock */ const char * test_flopen_lock_child(void) { const char *fn = "test_flopen_lock_child"; const char *result = NULL; pid_t pid; volatile int fd1, fd2; unlink(fn); fd1 = flopen(fn, O_RDWR|O_CREAT, 0640); if (fd1 < 0) { result = strerror(errno); } else { pid = fork(); if (pid == -1) { result = strerror(errno); } else if (pid == 0) { select(0, 0, 0, 0, 0); _exit(0); } close(fd1); if ((fd2 = flopen(fn, O_RDWR|O_NONBLOCK)) != -1) { result = "second open succeeded"; close(fd2); } kill(pid, SIGINT); } unlink(fn); return (result); } static struct test { const char *name; const char *(*func)(void); } t[] = { { "flopen_create", test_flopen_create }, { "flopen_open", test_flopen_open }, { "flopen_lock_self", test_flopen_lock_self }, { "flopen_lock_other", test_flopen_lock_other }, { "flopen_lock_child", test_flopen_lock_child }, }; int main(void) { const char *result; int i, nt; nt = sizeof(t) / sizeof(*t); printf("1..%d\n", nt); for (i = 0; i < nt; ++i) { if ((result = t[i].func()) != NULL) printf("not ok %d - %s # %s\n", i + 1, t[i].name, result); else printf("ok %d - %s\n", i + 1, t[i].name); } exit(0); } diff --git a/lib/libutil/tests/pidfile_test.c b/lib/libutil/tests/pidfile_test.c index 0463b7b29c87..bb11d6a19b79 100644 --- a/lib/libutil/tests/pidfile_test.c +++ b/lib/libutil/tests/pidfile_test.c @@ -1,327 +1,327 @@ /*- - * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2007-2009 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * We need a signal handler so kill(2) will interrupt the child * instead of killing it. */ static void signal_handler(int sig) { (void)sig; } /* * Test that pidfile_open() can create a pidfile and that pidfile_write() * can write to it. */ static const char * test_pidfile_uncontested(void) { const char *fn = "test_pidfile_uncontested"; struct pidfh *pf; pid_t other = 0; unlink(fn); pf = pidfile_open(fn, 0600, &other); if (pf == NULL && other != 0) return ("pidfile exists and is locked"); if (pf == NULL) return (strerror(errno)); if (pidfile_write(pf) != 0) { pidfile_close(pf); unlink(fn); return ("failed to write PID"); } pidfile_close(pf); unlink(fn); return (NULL); } /* * Test that pidfile_open() locks against self. */ static const char * test_pidfile_self(void) { const char *fn = "test_pidfile_self"; struct pidfh *pf1, *pf2; pid_t other = 0; int serrno; unlink(fn); pf1 = pidfile_open(fn, 0600, &other); if (pf1 == NULL && other != 0) return ("pidfile exists and is locked"); if (pf1 == NULL) return (strerror(errno)); if (pidfile_write(pf1) != 0) { serrno = errno; pidfile_close(pf1); unlink(fn); return (strerror(serrno)); } // second open should fail pf2 = pidfile_open(fn, 0600, &other); if (pf2 != NULL) { pidfile_close(pf1); pidfile_close(pf2); unlink(fn); return ("managed to opened pidfile twice"); } if (other != getpid()) { pidfile_close(pf1); unlink(fn); return ("pidfile contained wrong PID"); } pidfile_close(pf1); unlink(fn); return (NULL); } /* * Common code for test_pidfile_{contested,inherited}. */ static const char * common_test_pidfile_child(const char *fn, int parent_open) { struct pidfh *pf = NULL; pid_t other = 0, pid = 0; int fd[2], serrno, status; struct kevent event, ke; char ch; int kq; unlink(fn); if (pipe(fd) != 0) return (strerror(errno)); if (parent_open) { pf = pidfile_open(fn, 0600, &other); if (pf == NULL && other != 0) return ("pidfile exists and is locked"); if (pf == NULL) return (strerror(errno)); } pid = fork(); if (pid == -1) return (strerror(errno)); if (pid == 0) { // child close(fd[0]); signal(SIGINT, signal_handler); if (!parent_open) { pf = pidfile_open(fn, 0600, &other); if (pf == NULL && other != 0) return ("pidfile exists and is locked"); if (pf == NULL) return (strerror(errno)); } if (pidfile_write(pf) != 0) { serrno = errno; pidfile_close(pf); unlink(fn); return (strerror(serrno)); } if (pf == NULL) _exit(1); if (pidfile_write(pf) != 0) _exit(2); kq = kqueue(); if (kq == -1) _exit(3); EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); /* Attach event to the kqueue. */ if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0) _exit(4); /* Inform the parent we are ready to receive SIGINT */ if (write(fd[1], "*", 1) != 1) _exit(5); /* Wait for SIGINT received */ if (kevent(kq, NULL, 0, &event, 1, NULL) != 1) _exit(6); _exit(0); } // parent close(fd[1]); if (pf) pidfile_close(pf); // wait for the child to signal us if (read(fd[0], &ch, 1) != 1) { serrno = errno; unlink(fn); kill(pid, SIGTERM); errno = serrno; return (strerror(errno)); } // We shouldn't be able to lock the same pidfile as our child pf = pidfile_open(fn, 0600, &other); if (pf != NULL) { pidfile_close(pf); unlink(fn); return ("managed to lock contested pidfile"); } // Failed to lock, but not because it was contested if (other == 0) { unlink(fn); return (strerror(errno)); } // Locked by the wrong process if (other != pid) { unlink(fn); return ("pidfile contained wrong PID"); } // check our child's fate if (pf) pidfile_close(pf); unlink(fn); if (kill(pid, SIGINT) != 0) return (strerror(errno)); if (waitpid(pid, &status, 0) == -1) return (strerror(errno)); if (WIFSIGNALED(status)) return ("child caught signal"); if (WEXITSTATUS(status) != 0) return ("child returned non-zero status"); // success return (NULL); } /* * Test that pidfile_open() fails when attempting to open a pidfile that * is already locked, and that it returns the correct PID. */ static const char * test_pidfile_contested(void) { const char *fn = "test_pidfile_contested"; const char *result; result = common_test_pidfile_child(fn, 0); return (result); } /* * Test that the pidfile lock is inherited. */ static const char * test_pidfile_inherited(void) { const char *fn = "test_pidfile_inherited"; const char *result; result = common_test_pidfile_child(fn, 1); return (result); } /* * Make sure we handle relative pidfile paths correctly. */ static const char * test_pidfile_relative(void) { char path[PATH_MAX], pid[32], tmpdir[PATH_MAX]; struct pidfh *pfh; int fd; (void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__); if (mkdtemp(tmpdir) == NULL) return (strerror(errno)); (void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir); pfh = pidfile_open(path, 0600, NULL); if (pfh == NULL) return (strerror(errno)); if (pidfile_write(pfh) != 0) return (strerror(errno)); fd = open(path, O_RDONLY); if (fd < 0) return (strerror(errno)); memset(pid, 0, sizeof(pid)); if (read(fd, pid, sizeof(pid) - 1) < 0) return (strerror(errno)); if (atoi(pid) != getpid()) return ("pid mismatch"); if (close(fd) != 0) return (strerror(errno)); if (pidfile_close(pfh) != 0) return (strerror(errno)); return (NULL); } static struct test { const char *name; const char *(*func)(void); } t[] = { { "pidfile_uncontested", test_pidfile_uncontested }, { "pidfile_self", test_pidfile_self }, { "pidfile_contested", test_pidfile_contested }, { "pidfile_inherited", test_pidfile_inherited }, { "pidfile_relative", test_pidfile_relative }, }; int main(void) { const char *result; int i, nt; nt = sizeof(t) / sizeof(*t); printf("1..%d\n", nt); for (i = 0; i < nt; ++i) { if ((result = t[i].func()) != NULL) printf("not ok %d - %s # %s\n", i + 1, t[i].name, result); else printf("ok %d - %s\n", i + 1, t[i].name); } exit(0); } diff --git a/share/man/man4/aio.4 b/share/man/man4/aio.4 index e7b04544ba5e..21332a3b1037 100644 --- a/share/man/man4/aio.4 +++ b/share/man/man4/aio.4 @@ -1,237 +1,237 @@ .\"- -.\" Copyright (c) 2002 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2002 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd January 2, 2021 .Dt AIO 4 .Os .Sh NAME .Nm aio .Nd asynchronous I/O .Sh DESCRIPTION The .Nm facility provides system calls for asynchronous I/O. Asynchronous I/O operations are not completed synchronously by the calling thread. Instead, the calling thread invokes one system call to request an asynchronous I/O operation. The status of a completed request is retrieved later via a separate system call. .Pp Asynchronous I/O operations on some file descriptor types may block an AIO daemon indefinitely resulting in process and/or system hangs. Operations on these file descriptor types are considered .Dq unsafe and disabled by default. They can be enabled by setting the .Va vfs.aio.enable_unsafe sysctl node to a non-zero value. .Pp Asynchronous I/O operations on sockets, raw disk devices, and regular files on local filesystems do not block indefinitely and are always enabled. .Pp The .Nm facility uses kernel processes (also known as AIO daemons) to service most asynchronous I/O requests. These processes are grouped into pools containing a variable number of processes. Each pool will add or remove processes to the pool based on load. Pools can be configured by sysctl nodes that define the minimum and maximum number of processes as well as the amount of time an idle process will wait before exiting. .Pp One pool of AIO daemons is used to service asynchronous I/O requests for sockets. These processes are named .Dq soaiod . The following sysctl nodes are used with this pool: .Bl -tag -width indent .It Va kern.ipc.aio.num_procs The current number of processes in the pool. .It Va kern.ipc.aio.target_procs The minimum number of processes that should be present in the pool. .It Va kern.ipc.aio.max_procs The maximum number of processes permitted in the pool. .It Va kern.ipc.aio.lifetime The amount of time a process is permitted to idle in clock ticks. If a process is idle for this amount of time and there are more processes in the pool than the target minimum, the process will exit. .El .Pp A second pool of AIO daemons is used to service all other asynchronous I/O requests except for I/O requests to raw disks. These processes are named .Dq aiod . The following sysctl nodes are used with this pool: .Bl -tag -width indent .It Va vfs.aio.num_aio_procs The current number of processes in the pool. .It Va vfs.aio.target_aio_procs The minimum number of processes that should be present in the pool. .It Va vfs.aio.max_aio_procs The maximum number of processes permitted in the pool. .It Va vfs.aio.aiod_lifetime The amount of time a process is permitted to idle in clock ticks. If a process is idle for this amount of time and there are more processes in the pool than the target minimum, the process will exit. .El .Pp Asynchronous I/O requests for raw disks are queued directly to the disk device layer after temporarily wiring the user pages associated with the request. These requests are not serviced by any of the AIO daemon pools. .Pp Several limits on the number of asynchronous I/O requests are imposed both system-wide and per-process. These limits are configured via the following sysctls: .Bl -tag -width indent .It Va vfs.aio.max_buf_aio The maximum number of queued asynchronous I/O requests for raw disks permitted for a single process. Asynchronous I/O requests that have completed but whose status has not been retrieved via .Xr aio_return 2 or .Xr aio_waitcomplete 2 are not counted against this limit. .It Va vfs.aio.num_buf_aio The number of queued asynchronous I/O requests for raw disks system-wide. .It Va vfs.aio.max_aio_queue_per_proc The maximum number of asynchronous I/O requests for a single process serviced concurrently by the default AIO daemon pool. .It Va vfs.aio.max_aio_per_proc The maximum number of outstanding asynchronous I/O requests permitted for a single process. This includes requests that have not been serviced, requests currently being serviced, and requests that have completed but whose status has not been retrieved via .Xr aio_return 2 or .Xr aio_waitcomplete 2 . .It Va vfs.aio.num_queue_count The number of outstanding asynchronous I/O requests system-wide. .It Va vfs.aio.max_aio_queue The maximum number of outstanding asynchronous I/O requests permitted system-wide. .El .Pp Asynchronous I/O control buffers should be zeroed before initializing individual fields. This ensures all fields are initialized. .Pp All asynchronous I/O control buffers contain a .Vt sigevent structure in the .Va aio_sigevent field which can be used to request notification when an operation completes. .Pp For .Dv SIGEV_KEVENT notifications, the .Va sigevent .Ap s .Va sigev_notify_kqueue field should contain the descriptor of the kqueue that the event should be attached to, its .Va sigev_notify_kevent_flags field may contain .Dv EV_ONESHOT , .Dv EV_CLEAR , and/or .Dv EV_DISPATCH , and its .Va sigev_notify field should be set to .Dv SIGEV_KEVENT . The posted kevent will contain: .Bl -column ".Va filter" .It Sy Member Ta Sy Value .It Va ident Ta asynchronous I/O control buffer pointer .It Va filter Ta Dv EVFILT_AIO .It Va flags Ta Dv EV_EOF .It Va udata Ta value stored in .Va aio_sigevent.sigev_value .El .Pp For .Dv SIGEV_SIGNO and .Dv SIGEV_THREAD_ID notifications, the information for the queued signal will include .Dv SI_ASYNCIO in the .Va si_code field and the value stored in .Va sigevent.sigev_value in the .Va si_value field. .Pp For .Dv SIGEV_THREAD notifications, the value stored in .Va aio_sigevent.sigev_value is passed to the .Va aio_sigevent.sigev_notify_function as described in .Xr sigevent 3 . .Sh SEE ALSO .Xr aio_cancel 2 , .Xr aio_error 2 , .Xr aio_read 2 , .Xr aio_readv 2 , .Xr aio_return 2 , .Xr aio_suspend 2 , .Xr aio_waitcomplete 2 , .Xr aio_write 2 , .Xr aio_writev 2 , .Xr lio_listio 2 , .Xr sigevent 3 , .Xr sysctl 8 .Sh HISTORY The .Nm facility appeared as a kernel option in .Fx 3.0 . The .Nm kernel module appeared in .Fx 5.0 . The .Nm facility was integrated into all kernels in .Fx 11.0 . diff --git a/share/man/man4/coretemp.4 b/share/man/man4/coretemp.4 index 54948c2da13a..210b9d0fae62 100644 --- a/share/man/man4/coretemp.4 +++ b/share/man/man4/coretemp.4 @@ -1,73 +1,73 @@ .\"- -.\" Copyright (c) 2007 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2007 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd August 23, 2007 .Dt CORETEMP 4 .Os .Sh NAME .Nm coretemp .Nd device driver for Intel Core on-die digital thermal sensor .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device coretemp" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent coretemp_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides support for the on-die digital thermal sensor present in Intel Core and newer CPUs. .Pp The .Nm driver reports each core's temperature through a sysctl node in the corresponding CPU device's sysctl tree, named .Va dev.cpu.%d.temperature . .Sh SEE ALSO .Xr amdtemp 4 , .Xr sysctl 8 .Sh HISTORY The .Nm driver first appeared in .Fx 7.0 . .Sh AUTHORS .An -nosplit The .Nm driver was written by .An Rui Paulo Aq Mt rpaulo@FreeBSD.org as part of a Google Summer of Code project. This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/share/man/man4/ichwd.4 b/share/man/man4/ichwd.4 index f2a931405456..cc6924f7f866 100644 --- a/share/man/man4/ichwd.4 +++ b/share/man/man4/ichwd.4 @@ -1,88 +1,88 @@ .\"- -.\" Copyright (c) 2007 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2007 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd August 20, 2008 .Dt ICHWD 4 .Os .Sh NAME .Nm ichwd .Nd device driver for the Intel ICH watchdog interrupt timer .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device ichwd" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent ichwd_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides .Xr watchdog 4 support for the watchdog interrupt timer present on all Intel ICH motherboard chipsets. .Pp The ICH WDT counts down in ticks of approximately 0.6 seconds; the exact value depends on hardware quality and environmental factors. Supported watchdog intervals range from 2 to 63 ticks. .Pp Note that on some ICH-based systems, the WDT may be present but disabled, either in hardware or by the BIOS. The .Nm driver attempts to detect this condition and will refuse to attach if it believes the WDT is disabled. .Sh SEE ALSO .Xr watchdog 4 , .Xr watchdog 8 , .Xr watchdogd 8 , .Xr watchdog 9 .Rs .%T Using the Intel ICH Family Watchdog Timer (WDT) .%R Intel Application Note AP-725 .%O Document Number 292273-001 .Re .Sh HISTORY The .Nm driver first appeared in .Fx 5.3 . .Sh AUTHORS .An -nosplit The .Nm driver was written by .An Wm. Daryl Hawkins Aq Mt dhawkins@tamu.edu of Texas A&M University and .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/share/man/man4/ucycom.4 b/share/man/man4/ucycom.4 index 056b220f6d02..4f4790f51c03 100644 --- a/share/man/man4/ucycom.4 +++ b/share/man/man4/ucycom.4 @@ -1,101 +1,101 @@ .\"- -.\" Copyright (c) 2004 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2004 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd April 26, 2017 .Dt UCYCOM 4 .Os .Sh NAME .Nm ucycom .Nd device driver for Cypress CY7C63743 and CY7C64013 USB to RS232 bridges .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device usb" .Cd "device hid" .Cd "device ucom" .Cd "device ucycom" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent ucycom_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides support for the Cypress CY7C63743 and CY7C64013 bridge chips. These chips were designed to provide a low-cost transition path to USB for existing RS232 devices, and have fairly limited capabilities. .Pp The .Nm driver behaves like a .Xr tty 4 . .Sh HARDWARE The .Nm driver currently supports the following devices which incorporate Cypress USB to RS232 bridge chips: .Pp .Bl -bullet -compact .It DeLorme Earthmate USB GPS receiver .El .Sh FILES .Bl -tag -width "/dev/ttyU*.init" -compact .It Pa /dev/ttyU* for callin ports .It Pa /dev/ttyU*.init .It Pa /dev/ttyU*.lock corresponding callin initial-state and lock-state devices .Pp .It Pa /dev/cuaU* for callout ports .It Pa /dev/cuaU*.init .It Pa /dev/cuaU*.lock corresponding callout initial-state and lock-state devices .El .Sh SEE ALSO .Xr tty 4 , .Xr ucom 4 , .Xr usb 4 .Sh HISTORY The .Nm driver first appeared in .Fx 5.3 . .Sh AUTHORS .An -nosplit The .Nm driver and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/share/man/man9/LOCK_PROFILING.9 b/share/man/man9/LOCK_PROFILING.9 index 94f9596a63e9..e2b42cadb08b 100644 --- a/share/man/man9/LOCK_PROFILING.9 +++ b/share/man/man9/LOCK_PROFILING.9 @@ -1,185 +1,185 @@ .\"- -.\" Copyright (c) 2004 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2004 Dag-Erling Smørgrav .\" Copyright (c) 2005 Robert N. M. Watson .\" Copyright (c) 2006 Kip Macy .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd March 7, 2012 .Dt LOCK_PROFILING 9 .Os .Sh NAME .Nm LOCK_PROFILING .Nd kernel lock profiling support .Sh SYNOPSIS .Cd "options LOCK_PROFILING" .Sh DESCRIPTION The .Dv LOCK_PROFILING kernel option adds support for measuring and reporting lock use and contention statistics. These statistics are collated by .Dq acquisition point . Acquisition points are distinct places in the kernel source code (identified by source file name and line number) where a lock is acquired. .Pp For each acquisition point, the following statistics are accumulated: .Bl -bullet .It The longest time the lock was ever continuously held after being acquired at this point. .It The total time the lock was held after being acquired at this point. .It The total time that threads have spent waiting to acquire the lock. .It The total number of non-recursive acquisitions. .It The total number of times the lock was already held by another thread when this point was reached, requiring a spin or a sleep. .It The total number of times another thread tried to acquire the lock while it was held after having been acquired at this point. .El .Pp In addition, the average hold time and average wait time are derived from the total hold time and total wait time respectively and the number of acquisitions. .Pp The .Dv LOCK_PROFILING kernel option also adds the following .Xr sysctl 8 variables to control and monitor the profiling code: .Bl -tag -width indent .It Va debug.lock.prof.enable Enable or disable the lock profiling code. This defaults to 0 (off). .It Va debug.lock.prof.reset Reset the current lock profiling buffers. .It Va debug.lock.prof.stats The actual profiling statistics in plain text. The columns are as follows, from left to right: .Bl -tag -width ".Va cnt_hold" .It Va max The longest continuous hold time in microseconds. .It Va wait_max The longest continuous wait time in microseconds. .It Va total The total (accumulated) hold time in microseconds. .It Va wait_total The total (accumulated) wait time in microseconds. .It Va count The total number of acquisitions. .It Va avg The average hold time in microseconds, derived from the total hold time and the number of acquisitions. .It Va wait_avg The average wait time in microseconds, derived from the total wait time and the number of acquisitions. .It Va cnt_hold The number of times the lock was held and another thread attempted to acquire the lock. .It Va cnt_lock The number of times the lock was already held when this point was reached. .It Va name The name of the acquisition point, derived from the source file name and line number, followed by the name of the lock in parentheses. .El .It Va debug.lock.prof.rejected The number of acquisition points that were ignored after the table filled up. .It Va debug.lock.prof.skipspin Disable or enable the lock profiling code for the spin locks. This defaults to 0 (do profiling for the spin locks). .It Va debug.lock.prof.skipcount Do sampling approximately every N lock acquisitions. .El .Sh SEE ALSO .Xr sysctl 8 , .Xr mutex 9 .Sh HISTORY Mutex profiling support appeared in .Fx 5.0 . Generalized lock profiling support appeared in .Fx 7.0 . .Sh AUTHORS .An -nosplit The .Nm MUTEX_PROFILING code was written by .An Eivind Eklund Aq Mt eivind@FreeBSD.org , .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org and .An Robert Watson Aq Mt rwatson@FreeBSD.org . The .Nm code was written by .An Kip Macy Aq Mt kmacy@FreeBSD.org . This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . .Sh NOTES The .Dv LOCK_PROFILING option increases the size of .Vt "struct lock_object" , so a kernel built with that option will not work with modules built without it. .Pp The .Dv LOCK_PROFILING option also prevents inlining of the mutex code, which can result in a fairly severe performance penalty. This is, however, not always the case. .Dv LOCK_PROFILING can introduce a substantial performance overhead that is easily monitorable using other profiling tools, so combining profiling tools with .Dv LOCK_PROFILING is not recommended. .Pp Measurements are made and stored in nanoseconds using .Xr nanotime 9 , (on architectures without a synchronized TSC) but are presented in microseconds. This should still be sufficient for the locks one would be most interested in profiling (those that are held long and/or acquired often). .Pp .Dv LOCK_PROFILING should generally not be used in combination with other debugging options, as the results may be strongly affected by interactions between the features. In particular, .Dv LOCK_PROFILING will report higher than normal .Xr uma 9 lock contention when run with .Dv INVARIANTS due to extra locking that occurs when .Dv INVARIANTS is present; likewise, using it in combination with .Dv WITNESS will lead to much higher lock hold times and contention in profiling output. diff --git a/share/man/man9/pseudofs.9 b/share/man/man9/pseudofs.9 index 54b80b5b95e5..e076c398c711 100644 --- a/share/man/man9/pseudofs.9 +++ b/share/man/man9/pseudofs.9 @@ -1,68 +1,68 @@ .\"- -.\" Copyright (c) 2001 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2001 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd April 20, 2007 .Dt PSEUDOFS 9 .Os .Sh NAME .Nm pseudofs .Nd pseudo file system construction kit .Sh SYNOPSIS .In fs/pseudofs/pseudofs.h .\" Insert usage example here .Sh DESCRIPTION The .Nm module offers an abstract API for pseudo-file systems such as .Xr procfs 5 and .Xr linprocfs 5 . It takes care of all the hairy bits like interfacing with the VFS system, enforcing access control, keeping track of file numbers, and cloning files and directories that are process-specific. The consumer module, i.e., the module that implements the actual guts of the file system, needs only provide the directory structure (represented by a collection of structures declared and initialized by macros provided by .Nm ) and callbacks that report file attributes or write the actual file contents into sbufs. .\" Insert more info here .Sh SEE ALSO .Xr linprocfs 5 , .Xr linsysfs 5 , .Xr procfs 5 , .Xr sbuf 9 , .Xr vnode 9 .Sh HISTORY The .Nm module appeared in .Fx 5.0 . .Sh AUTHORS The .Nm module and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/share/man/man9/sbuf.9 b/share/man/man9/sbuf.9 index 499b9b194d35..5ab045118f51 100644 --- a/share/man/man9/sbuf.9 +++ b/share/man/man9/sbuf.9 @@ -1,767 +1,767 @@ .\"- -.\" Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd August 26, 2020 .Dt SBUF 9 .Os .Sh NAME .Nm sbuf , .Nm sbuf_new , .Nm sbuf_new_auto , .Nm sbuf_new_for_sysctl , .Nm sbuf_clear , .Nm sbuf_get_flags , .Nm sbuf_set_flags , .Nm sbuf_clear_flags , .Nm sbuf_setpos , .Nm sbuf_bcat , .Nm sbuf_bcopyin , .Nm sbuf_bcpy , .Nm sbuf_cat , .Nm sbuf_copyin , .Nm sbuf_cpy , .Nm sbuf_nl_terminate , .Nm sbuf_printf , .Nm sbuf_vprintf , .Nm sbuf_putc , .Nm sbuf_set_drain , .Nm sbuf_trim , .Nm sbuf_error , .Nm sbuf_finish , .Nm sbuf_data , .Nm sbuf_len , .Nm sbuf_done , .Nm sbuf_delete , .Nm sbuf_start_section , .Nm sbuf_end_section , .Nm sbuf_hexdump , .Nm sbuf_printf_drain , .Nm sbuf_putbuf .Nd safe string composition .Sh SYNOPSIS .In sys/types.h .In sys/sbuf.h .Ft typedef int .Fo (sbuf_drain_func) .Fa "void *arg" .Fa "const char *data" .Fa "int len" .Fc .Pp .Ft struct sbuf * .Fo sbuf_new .Fa "struct sbuf *s" .Fa "char *buf" .Fa "int length" .Fa "int flags" .Fc .Ft struct sbuf * .Fo sbuf_new_auto .Fa "void" .Fc .Ft void .Fo sbuf_clear .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_get_flags .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_set_flags .Fa "struct sbuf *s" .Fa "int flags" .Fc .Ft void .Fo sbuf_clear_flags .Fa "struct sbuf *s" .Fa "int flags" .Fc .Ft int .Fo sbuf_setpos .Fa "struct sbuf *s" .Fa "int pos" .Fc .Ft int .Fo sbuf_bcat .Fa "struct sbuf *s" .Fa "const void *buf" .Fa "size_t len" .Fc .Ft int .Fo sbuf_bcpy .Fa "struct sbuf *s" .Fa "const void *buf" .Fa "size_t len" .Fc .Ft int .Fo sbuf_cat .Fa "struct sbuf *s" .Fa "const char *str" .Fc .Ft int .Fo sbuf_cpy .Fa "struct sbuf *s" .Fa "const char *str" .Fc .Ft int .Fn sbuf_nl_terminate "struct sbuf *" .Ft int .Fo sbuf_printf .Fa "struct sbuf *s" .Fa "const char *fmt" "..." .Fc .Ft int .Fo sbuf_vprintf .Fa "struct sbuf *s" .Fa "const char *fmt" .Fa "va_list ap" .Fc .Ft int .Fo sbuf_putc .Fa "struct sbuf *s" .Fa "int c" .Fc .Ft void .Fo sbuf_set_drain .Fa "struct sbuf *s" .Fa "sbuf_drain_func *func" .Fa "void *arg" .Fc .Ft int .Fo sbuf_trim .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_error .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_finish .Fa "struct sbuf *s" .Fc .Ft char * .Fo sbuf_data .Fa "struct sbuf *s" .Fc .Ft ssize_t .Fo sbuf_len .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_done .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_delete .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_start_section .Fa "struct sbuf *s" .Fa "ssize_t *old_lenp" .Fc .Ft ssize_t .Fo sbuf_end_section .Fa "struct sbuf *s" .Fa "ssize_t old_len" .Fa "size_t pad" .Fa "int c" .Fc .Ft void .Fo sbuf_hexdump .Fa "struct sbuf *sb" .Fa "void *ptr" .Fa "int length" .Fa "const char *hdr" .Fa "int flags" .Fc .Ft int .Fo sbuf_printf_drain .Fa "void *arg" .Fa "const char *data" .Fa "int len" .Fc .Ft void .Fo sbuf_putbuf .Fa "struct sbuf *s" .Fc .Fd #ifdef _KERNEL .In sys/types.h .In sys/sbuf.h .Ft int .Fo sbuf_bcopyin .Fa "struct sbuf *s" .Fa "const void *uaddr" .Fa "size_t len" .Fc .Ft int .Fo sbuf_copyin .Fa "struct sbuf *s" .Fa "const void *uaddr" .Fa "size_t len" .Fc .In sys/sysctl.h .Ft struct sbuf * .Fo sbuf_new_for_sysctl .Fa "struct sbuf *s" .Fa "char *buf" .Fa "int length" .Fa "struct sysctl_req *req" .Fc .Fd #endif /* _KERNEL */ .Sh DESCRIPTION The .Nm family of functions allows one to safely allocate, compose and release strings in kernel or user space. .Pp Instead of arrays of characters, these functions operate on structures called .Fa sbufs , defined in .In sys/sbuf.h . .Pp Any errors encountered during the allocation or composition of the string will be latched in the data structure, making a single error test at the end of the composition sufficient to determine success or failure of the entire process. .Pp The .Fn sbuf_new function initializes the .Fa sbuf pointed to by its first argument. If that pointer is .Dv NULL , .Fn sbuf_new allocates a .Vt struct sbuf using .Xr malloc 9 . The .Fa buf argument is a pointer to a buffer in which to store the actual string; if it is .Dv NULL , .Fn sbuf_new will allocate one using .Xr malloc 9 . The .Fa length is the initial size of the storage buffer. The fourth argument, .Fa flags , may be comprised of the following flags: .Bl -tag -width ".Dv SBUF_AUTOEXTEND" .It Dv SBUF_FIXEDLEN The storage buffer is fixed at its initial size. Attempting to extend the sbuf beyond this size results in an overflow condition. .It Dv SBUF_AUTOEXTEND This indicates that the storage buffer may be extended as necessary, so long as resources allow, to hold additional data. .It Dv SBUF_INCLUDENUL This causes the final nulterm byte to be counted in the length of the data. .It Dv SBUF_DRAINTOEOR Treat top-level sections started with .Fn sbuf_start_section as a record boundary marker that will be used during drain operations to avoid records being split. If a record grows sufficiently large such that it fills the .Fa sbuf and therefore cannot be drained without being split, an error of .Er EDEADLK is set. .It Dv SBUF_NOWAIT Indicates that attempts to extend the storage buffer should fail in low memory conditions, like .Xr malloc 9 .Dv M_NOWAIT . .El .Pp Note that if .Fa buf is not .Dv NULL , it must point to an array of at least .Fa length characters. The result of accessing that array directly while it is in use by the sbuf is undefined. .Pp The .Fn sbuf_new_auto function is a shortcut for creating a completely dynamic .Nm . It is the equivalent of calling .Fn sbuf_new with values .Dv NULL , .Dv NULL , .Dv 0 , and .Dv SBUF_AUTOEXTEND . .Pp The .Fn sbuf_new_for_sysctl function will set up an sbuf with a drain function to use .Fn SYSCTL_OUT when the internal buffer fills. Note that if the various functions which append to an sbuf are used while a non-sleepable lock is held, the user buffer should be wired using .Fn sysctl_wire_old_buffer . .Pp The .Fn sbuf_delete function clears the .Fa sbuf and frees any memory allocated for it. There must be a call to .Fn sbuf_delete for every call to .Fn sbuf_new . Any attempt to access the sbuf after it has been deleted will fail. .Pp The .Fn sbuf_clear function invalidates the contents of the .Fa sbuf and resets its position to zero. .Pp The .Fn sbuf_get_flags function returns the current user flags. The .Fn sbuf_set_flags and .Fn sbuf_clear_flags functions set or clear one or more user flags, respectively. The user flags are described under the .Fn sbuf_new function. .Pp The .Fn sbuf_setpos function sets the .Fa sbuf Ns 's end position to .Fa pos , which is a value between zero and the current position in the buffer. It can only truncate the sbuf to the new position. .Pp The .Fn sbuf_bcat function appends the first .Fa len bytes from the buffer .Fa buf to the .Fa sbuf . .Pp The .Fn sbuf_bcopyin function copies .Fa len bytes from the specified userland address into the .Fa sbuf . .Pp The .Fn sbuf_bcpy function replaces the contents of the .Fa sbuf with the first .Fa len bytes from the buffer .Fa buf . .Pp The .Fn sbuf_cat function appends the NUL-terminated string .Fa str to the .Fa sbuf at the current position. .Pp The .Fn sbuf_set_drain function sets a drain function .Fa func for the .Fa sbuf , and records a pointer .Fa arg to be passed to the drain on callback. The drain function cannot be changed while .Fa sbuf_len is non-zero. .Pp The registered drain function .Vt sbuf_drain_func will be called with the argument .Fa arg provided to .Fn sbuf_set_drain , a pointer .Fa data to a byte string that is the contents of the sbuf, and the length .Fa len of the data. If the drain function exists, it will be called when the sbuf internal buffer is full, or on behalf of .Fn sbuf_finish . The drain function may drain some or all of the data, but must drain at least 1 byte. The return value from the drain function, if positive, indicates how many bytes were drained. If negative, the return value indicates the negative error code which will be returned from this or a later call to .Fn sbuf_finish . If the returned drained length is 0, an error of .Er EDEADLK is set. To do unbuffered draining, initialize the sbuf with a two-byte buffer. The drain will be called for every byte added to the sbuf. The .Fn sbuf_bcopyin , .Fn sbuf_bcpy , .Fn sbuf_clear , .Fn sbuf_copyin , .Fn sbuf_cpy , .Fn sbuf_trim , .Fn sbuf_data , and .Fn sbuf_len functions cannot be used on an sbuf with a drain. .Pp The .Fn sbuf_copyin function copies a NUL-terminated string from the specified userland address into the .Fa sbuf . If the .Fa len argument is non-zero, no more than .Fa len characters (not counting the terminating NUL) are copied; otherwise the entire string, or as much of it as can fit in the .Fa sbuf , is copied. .Pp The .Fn sbuf_cpy function replaces the contents of the .Fa sbuf with those of the NUL-terminated string .Fa str . This is equivalent to calling .Fn sbuf_cat with a fresh .Fa sbuf or one which position has been reset to zero with .Fn sbuf_clear or .Fn sbuf_setpos . .Pp The .Fn sbuf_nl_terminate function appends a trailing newline character, if the current line is non-empty and not already terminated by a newline character. .Pp The .Fn sbuf_printf function formats its arguments according to the format string pointed to by .Fa fmt and appends the resulting string to the .Fa sbuf at the current position. .Pp The .Fn sbuf_vprintf function behaves the same as .Fn sbuf_printf except that the arguments are obtained from the variable-length argument list .Fa ap . .Pp The .Fn sbuf_putc function appends the character .Fa c to the .Fa sbuf at the current position. .Pp The .Fn sbuf_trim function removes trailing whitespace from the .Fa sbuf . .Pp The .Fn sbuf_error function returns any error value that the .Fa sbuf may have accumulated, either from the drain function, or .Er ENOMEM if the .Fa sbuf overflowed. This function is generally not needed and instead the error code from .Fn sbuf_finish is the preferred way to discover whether an sbuf had an error. .Pp The .Fn sbuf_finish function will call the attached drain function if one exists until all the data in the .Fa sbuf is flushed. If there is no attached drain, .Fn sbuf_finish NUL-terminates the .Fa sbuf . In either case it marks the .Fa sbuf as finished, which means that it may no longer be modified using .Fn sbuf_setpos , .Fn sbuf_cat , .Fn sbuf_cpy , .Fn sbuf_printf or .Fn sbuf_putc , until .Fn sbuf_clear is used to reset the sbuf. .Pp The .Fn sbuf_data function returns the actual string; .Fn sbuf_data only works on a finished .Fa sbuf . The .Fn sbuf_len function returns the length of the string. For an .Fa sbuf with an attached drain, .Fn sbuf_len returns the length of the un-drained data. .Fn sbuf_done returns non-zero if the .Fa sbuf is finished. .Pp The .Fn sbuf_start_section and .Fn sbuf_end_section functions may be used for automatic section alignment. The arguments .Fa pad and .Fa c specify the padding size and a character used for padding. The arguments .Fa old_lenp and .Fa old_len are to save and restore the current section length when nested sections are used. For the top level section .Dv NULL and \-1 can be specified for .Fa old_lenp and .Fa old_len respectively. .Pp The .Fn sbuf_hexdump function prints an array of bytes to the supplied sbuf, along with an ASCII representation of the bytes if possible. See the .Xr hexdump 3 man page for more details on the interface. .Pp The .Fn sbuf_printf_drain function is a drain function that will call printf, or log to the console. The argument .Fa arg must be either .Dv NULL , or a valid pointer to a .Vt size_t . If .Fa arg is not .Dv NULL , the total bytes drained will be added to the value pointed to by .Fa arg . .Pp The .Fn sbuf_putbuf function printfs the sbuf to stdout if in userland, and to the console and log if in the kernel. The .Fa sbuf must be finished before calling .Fn sbuf_putbuf . It does not drain the buffer or update any pointers. .Sh NOTES If an operation caused an .Fa sbuf to overflow, most subsequent operations on it will fail until the .Fa sbuf is finished using .Fn sbuf_finish or reset using .Fn sbuf_clear , or its position is reset to a value between 0 and one less than the size of its storage buffer using .Fn sbuf_setpos , or it is reinitialized to a sufficiently short string using .Fn sbuf_cpy . .Pp Drains in user-space will not always function as indicated. While the drain function will be called immediately on overflow from the .Fa sbuf_putc , .Fa sbuf_bcat , .Fa sbuf_cat functions, .Fa sbuf_printf and .Fa sbuf_vprintf currently have no way to determine whether there will be an overflow until after it occurs, and cannot do a partial expansion of the format string. Thus when using libsbuf the buffer may be extended to allow completion of a single printf call, even though a drain is attached. .Sh RETURN VALUES The .Fn sbuf_new function returns .Dv NULL if it failed to allocate a storage buffer, and a pointer to the new .Fa sbuf otherwise. .Pp The .Fn sbuf_setpos function returns \-1 if .Fa pos was invalid, and zero otherwise. .Pp The .Fn sbuf_bcat , .Fn sbuf_cat , .Fn sbuf_cpy , .Fn sbuf_printf , .Fn sbuf_putc , and .Fn sbuf_trim functions all return \-1 if the buffer overflowed, and zero otherwise. .Pp The .Fn sbuf_error function returns a non-zero value if the buffer has an overflow or drain error, and zero otherwise. .Pp The .Fn sbuf_len function returns \-1 if the buffer overflowed. .Pp The .Fn sbuf_copyin function returns \-1 if copying string from userland failed, and number of bytes copied otherwise. .Pp The .Fn sbuf_end_section function returns the section length or \-1 if the buffer has an error. .Pp The .Fn sbuf_finish 9 function (the kernel version) returns .Er ENOMEM if the sbuf overflowed before being finished, or returns the error code from the drain if one is attached. .Pp The .Fn sbuf_finish 3 function (the userland version) will return zero for success and \-1 and set errno on error. .Sh EXAMPLES .Bd -literal -compact #include #include struct sbuf *sb; sb = sbuf_new_auto(); sbuf_cat(sb, "Customers found:\en"); TAILQ_FOREACH(foo, &foolist, list) { sbuf_printf(sb, " %4d %s\en", foo->index, foo->name); sbuf_printf(sb, " Address: %s\en", foo->address); sbuf_printf(sb, " Zip: %s\en", foo->zipcode); } if (sbuf_finish(sb) != 0) /* Check for any and all errors */ err(1, "Could not generate message"); transmit_msg(sbuf_data(sb), sbuf_len(sb)); sbuf_delete(sb); .Ed .Sh SEE ALSO .Xr hexdump 3 , .Xr printf 3 , .Xr strcat 3 , .Xr strcpy 3 , .Xr copyin 9 , .Xr copyinstr 9 , .Xr printf 9 .Sh HISTORY The .Nm family of functions first appeared in .Fx 4.4 . .Sh AUTHORS .An -nosplit The .Nm family of functions was designed by .An Poul-Henning Kamp Aq Mt phk@FreeBSD.org and implemented by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . Additional improvements were suggested by .An Justin T. Gibbs Aq Mt gibbs@FreeBSD.org . Auto-extend support added by .An Kelly Yancey Aq Mt kbyanc@FreeBSD.org . Drain functionality added by .An Matthew Fleming Aq Mt mdf@FreeBSD.org . .Pp This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/share/man/man9/zone.9 b/share/man/man9/zone.9 index 24cd2b4cb2d8..a6b8d2dfedbe 100644 --- a/share/man/man9/zone.9 +++ b/share/man/man9/zone.9 @@ -1,639 +1,639 @@ .\"- -.\" Copyright (c) 2001 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2001 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd January 16, 2023 .Dt UMA 9 .Os .Sh NAME .Nm UMA .Nd general-purpose kernel object allocator .Sh SYNOPSIS .In sys/param.h .In sys/queue.h .In vm/uma.h .Bd -literal typedef int (*uma_ctor)(void *mem, int size, void *arg, int flags); typedef void (*uma_dtor)(void *mem, int size, void *arg); typedef int (*uma_init)(void *mem, int size, int flags); typedef void (*uma_fini)(void *mem, int size); typedef int (*uma_import)(void *arg, void **store, int count, int domain, int flags); typedef void (*uma_release)(void *arg, void **store, int count); typedef void *(*uma_alloc)(uma_zone_t zone, vm_size_t size, int domain, uint8_t *pflag, int wait); typedef void (*uma_free)(void *item, vm_size_t size, uint8_t pflag); .Ed .Ft uma_zone_t .Fo uma_zcreate .Fa "char *name" "size_t size" .Fa "uma_ctor ctor" "uma_dtor dtor" "uma_init zinit" "uma_fini zfini" .Fa "int align" "uint16_t flags" .Fc .Ft uma_zone_t .Fo uma_zcache_create .Fa "char *name" "int size" .Fa "uma_ctor ctor" "uma_dtor dtor" "uma_init zinit" "uma_fini zfini" .Fa "uma_import zimport" "uma_release zrelease" .Fa "void *arg" "int flags" .Fc .Ft uma_zone_t .Fo uma_zsecond_create .Fa "char *name" .Fa "uma_ctor ctor" "uma_dtor dtor" "uma_init zinit" "uma_fini zfini" .Fa "uma_zone_t master" .Fc .Ft void .Fn uma_zdestroy "uma_zone_t zone" .Ft "void *" .Fn uma_zalloc "uma_zone_t zone" "int flags" .Ft "void *" .Fn uma_zalloc_arg "uma_zone_t zone" "void *arg" "int flags" .Ft "void *" .Fn uma_zalloc_domain "uma_zone_t zone" "void *arg" "int domain" "int flags" .Ft "void *" .Fn uma_zalloc_pcpu "uma_zone_t zone" "int flags" .Ft "void *" .Fn uma_zalloc_pcpu_arg "uma_zone_t zone" "void *arg" "int flags" .Ft "void *" .Fn uma_zalloc_smr "uma_zone_t zone" "int flags" .Ft void .Fn uma_zfree "uma_zone_t zone" "void *item" .Ft void .Fn uma_zfree_arg "uma_zone_t zone" "void *item" "void *arg" .Ft void .Fn uma_zfree_pcpu "uma_zone_t zone" "void *item" .Ft void .Fn uma_zfree_pcpu_arg "uma_zone_t zone" "void *item" "void *arg" .Ft void .Fn uma_zfree_smr "uma_zone_t zone" "void *item" .Ft void .Fn uma_prealloc "uma_zone_t zone" "int nitems" .Ft void .Fn uma_zone_reserve "uma_zone_t zone" "int nitems" .Ft void .Fn uma_zone_reserve_kva "uma_zone_t zone" "int nitems" .Ft void .Fn uma_reclaim "int req" .Ft void .Fn uma_reclaim_domain "int req" "int domain" .Ft void .Fn uma_zone_reclaim "uma_zone_t zone" "int req" .Ft void .Fn uma_zone_reclaim_domain "uma_zone_t zone" "int req" "int domain" .Ft void .Fn uma_zone_set_allocf "uma_zone_t zone" "uma_alloc allocf" .Ft void .Fn uma_zone_set_freef "uma_zone_t zone" "uma_free freef" .Ft int .Fn uma_zone_set_max "uma_zone_t zone" "int nitems" .Ft void .Fn uma_zone_set_maxcache "uma_zone_t zone" "int nitems" .Ft int .Fn uma_zone_get_max "uma_zone_t zone" .Ft int .Fn uma_zone_get_cur "uma_zone_t zone" .Ft void .Fn uma_zone_set_warning "uma_zone_t zone" "const char *warning" .Ft void .Fn uma_zone_set_maxaction "uma_zone_t zone" "void (*maxaction)(uma_zone_t)" .Ft smr_t .Fn uma_zone_get_smr "uma_zone_t zone" .Ft void .Fn uma_zone_set_smr "uma_zone_t zone" "smr_t smr" .In sys/sysctl.h .Fn SYSCTL_UMA_MAX parent nbr name access zone descr .Fn SYSCTL_ADD_UMA_MAX ctx parent nbr name access zone descr .Fn SYSCTL_UMA_CUR parent nbr name access zone descr .Fn SYSCTL_ADD_UMA_CUR ctx parent nbr name access zone descr .Sh DESCRIPTION UMA (Universal Memory Allocator) provides an efficient interface for managing dynamically-sized collections of items of identical size, referred to as zones. Zones keep track of which items are in use and which are not, and UMA provides functions for allocating items from a zone and for releasing them back, making them available for subsequent allocation requests. Zones maintain per-CPU caches with linear scalability on SMP systems as well as round-robin and first-touch policies for NUMA systems. The number of items cached per CPU is bounded, and each zone additionally maintains an unbounded cache of items that is used to quickly satisfy per-CPU cache allocation misses. .Pp Two types of zones exist: regular zones and cache zones. In a regular zone, items are allocated from a slab, which is one or more virtually contiguous memory pages that have been allocated from the kernel's page allocator. Internally, slabs are managed by a UMA keg, which is responsible for allocating slabs and keeping track of their usage by one or more zones. In typical usage, there is one keg per zone, so slabs are not shared among multiple zones. .Pp Normal zones import items from a keg, and release items back to that keg if requested. Cache zones do not have a keg, and instead use custom import and release methods. For example, some collections of kernel objects are statically allocated at boot-time, and the size of the collection does not change. A cache zone can be used to implement an efficient allocator for the objects in such a collection. .Pp The .Fn uma_zcreate and .Fn uma_zcache_create functions create a new regular zone and cache zone, respectively. The .Fn uma_zsecond_create function creates a regular zone which shares the keg of the zone specified by the .Fa master argument. The .Fa name argument is a text name of the zone for debugging and stats; this memory should not be freed until the zone has been deallocated. .Pp The .Fa ctor and .Fa dtor arguments are callback functions that are called by the UMA subsystem at the time of the call to .Fn uma_zalloc and .Fn uma_zfree respectively. Their purpose is to provide hooks for initializing or destroying things that need to be done at the time of the allocation or release of a resource. A good usage for the .Fa ctor and .Fa dtor callbacks might be to initialize a data structure embedded in the item, such as a .Xr queue 3 head. .Pp The .Fa zinit and .Fa zfini arguments are used to optimize the allocation of items from the zone. They are called by the UMA subsystem whenever it needs to allocate or free items to satisfy requests or memory pressure. A good use for the .Fa zinit and .Fa zfini callbacks might be to initialize and destroy a mutex contained within an item. This would allow one to avoid destroying and re-initializing the mutex each time the item is freed and re-allocated. They are not called on each call to .Fn uma_zalloc and .Fn uma_zfree but rather when an item is imported into a zone's cache, and when a zone releases an item to the slab allocator, typically as a response to memory pressure. .Pp For .Fn uma_zcache_create , the .Fa zimport and .Fa zrelease functions are called to import items into the zone and to release items from the zone, respectively. The .Fa zimport function should store pointers to items in the .Fa store array, which contains a maximum of .Fa count entries. The function must return the number of imported items, which may be less than the maximum. Similarly, the .Fa store parameter to the .Fa zrelease function contains an array of .Fa count pointers to items. The .Fa arg parameter passed to .Fn uma_zcache_create is provided to the import and release functions. The .Fa domain parameter to .Fa zimport specifies the requested .Xr numa 4 domain for the allocation. It is either a NUMA domain number or the special value .Dv UMA_ANYDOMAIN . .Pp The .Fa flags argument of .Fn uma_zcreate and .Fn uma_zcache_create is a subset of the following flags: .Bl -tag -width "foo" .It Dv UMA_ZONE_NOFREE Slabs allocated to the zone's keg are never freed. .It Dv UMA_ZONE_NODUMP Pages belonging to the zone will not be included in minidumps. .It Dv UMA_ZONE_PCPU An allocation from zone would have .Va mp_ncpu shadow copies, that are privately assigned to CPUs. A CPU can address its private copy using base the allocation address plus a multiple of the current CPU ID and .Fn sizeof "struct pcpu" : .Bd -literal -offset indent foo_zone = uma_zcreate(..., UMA_ZONE_PCPU); ... foo_base = uma_zalloc(foo_zone, ...); ... critical_enter(); foo_pcpu = (foo_t *)zpcpu_get(foo_base); /* do something with foo_pcpu */ critical_exit(); .Ed Note that .Dv M_ZERO cannot be used when allocating items from a PCPU zone. To obtain zeroed memory from a PCPU zone, use the .Fn uma_zalloc_pcpu function and its variants instead, and pass .Dv M_ZERO . .It Dv UMA_ZONE_NOTOUCH The UMA subsystem may not directly touch (i.e. read or write) the slab memory. Otherwise, by default, book-keeping of items within a slab may be done in the slab page itself, and .Dv INVARIANTS kernels may also do use-after-free checking by accessing the slab memory. .It Dv UMA_ZONE_ZINIT The zone will have its .Ft uma_init method set to internal method that initializes a new allocated slab to all zeros. Do not mistake .Ft uma_init method with .Ft uma_ctor . A zone with .Dv UMA_ZONE_ZINIT flag would not return zeroed memory on every .Fn uma_zalloc . .It Dv UMA_ZONE_NOTPAGE An allocator function will be supplied with .Fn uma_zone_set_allocf and the memory that it returns may not be kernel virtual memory backed by VM pages in the page array. .It Dv UMA_ZONE_MALLOC The zone is for the .Xr malloc 9 subsystem. .It Dv UMA_ZONE_VM The zone is for the VM subsystem. .It Dv UMA_ZONE_CONTIG Items in this zone must be contiguous in physical address space. Items will follow normal alignment constraints and may span page boundaries between pages with contiguous physical addresses. .It Dv UMA_ZONE_UNMANAGED By default, UMA zone caches are shrunk to help resolve free page shortages. Cached items that have not been used for a long period may also be freed from zone. When this flag is set, the system will not reclaim memory from the zone's caches. .It Dv UMA_ZONE_SMR Create a zone whose items will be synchronized using the .Xr smr 9 mechanism. Upon creation the zone will have an associated .Dt smr_t structure which can be fetched using .Fn uma_zone_get_smr . .El .Pp Zones can be destroyed using .Fn uma_zdestroy , freeing all memory that is cached in the zone. All items allocated from the zone must be freed to the zone before the zone may be safely destroyed. .Pp To allocate an item from a zone, simply call .Fn uma_zalloc with a pointer to that zone and set the .Fa flags argument to selected flags as documented in .Xr malloc 9 . It will return a pointer to an item if successful, or .Dv NULL in the rare case where all items in the zone are in use and the allocator is unable to grow the zone and .Dv M_NOWAIT is specified. .Pp Items are released back to the zone from which they were allocated by calling .Fn uma_zfree with a pointer to the zone and a pointer to the item. If .Fa item is .Dv NULL , then .Fn uma_zfree does nothing. .Pp The variants .Fn uma_zalloc_arg and .Fn uma_zfree_arg allow callers to specify an argument for the .Dv ctor and .Dv dtor functions of the zone, respectively. The variants .Fn uma_zalloc_pcpu and .Fn uma_zfree_pcpu allocate and free .Va mp_ncpu shadow copies as described for .Dv UMA_ZONE_PCPU . If .Fa item is .Dv NULL , then .Fn uma_zfree_pcpu does nothing. .Pp The .Fn uma_zalloc_smr and .Fn uma_zfree_smr functions allocate and free items from an SMR-enabled zone, that is, a zone created with .Dv UMA_ZONE_SMR or a zone that has had .Fn uma_zone_set_smr called. .Pp The .Fn uma_zalloc_domain function allows callers to specify a fixed .Xr numa 4 domain to allocate from. This uses a guaranteed but slow path in the allocator which reduces concurrency. .Pp The .Fn uma_prealloc function allocates slabs for the requested number of items, typically following the initial creation of a zone. Subsequent allocations from the zone will be satisfied using the pre-allocated slabs. Note that slab allocation is performed with the .Dv M_WAITOK flag, so .Fn uma_prealloc may sleep. .Pp The .Fn uma_zone_reserve function sets the number of reserved items for the zone. .Fn uma_zalloc and variants will ensure that the zone contains at least the reserved number of free items. Reserved items may be allocated by specifying .Dv M_USE_RESERVE in the allocation request flags. .Fn uma_zone_reserve does not perform any pre-allocation by itself. .Pp The .Fn uma_zone_reserve_kva function pre-allocates kernel virtual address space for the requested number of items. Subsequent allocations from the zone will be satisfied using the pre-allocated address space. Note that unlike .Fn uma_zone_reserve , .Fn uma_zone_reserve_kva does not restrict the use of the pre-allocation to .Dv M_USE_RESERVE requests. .Pp The .Fn uma_reclaim and .Fn uma_zone_reclaim functions reclaim cached items from UMA zones, releasing unused memory. The .Fn uma_reclaim function reclaims items from all regular zones, while .Fn uma_zone_reclaim reclaims items only from the specified zone. The .Fa req parameter must be one of three values which specify how aggressively items are to be reclaimed: .Bl -tag -width indent .It Dv UMA_RECLAIM_TRIM Reclaim items only in excess of the zone's estimated working set size. The working set size is periodically updated and tracks the recent history of the zone's usage. .It Dv UMA_RECLAIM_DRAIN Reclaim all items from the unbounded cache. Free items in the per-CPU caches are left alone. .It Dv UMA_RECLAIM_DRAIN_CPU Reclaim all cached items. .El The .Fn uma_reclaim_domain and .Fn uma_zone_reclaim_domain functions apply only to items allocated from the specified domain. In the case of domains using a round-robin NUMA policy, cached items from all domains are freed to the keg, but only slabs from the specific domain will be freed. .Pp The .Fn uma_zone_set_allocf and .Fn uma_zone_set_freef functions allow a zone's default slab allocation and free functions to be overridden. This is useful if memory with special constraints such as attributes, alignment, or address ranges must be used. .Pp The .Fn uma_zone_set_max function limits the number of items .Pq and therefore memory that can be allocated to .Fa zone . The .Fa nitems argument specifies the requested upper limit number of items. The effective limit is returned to the caller, as it may end up being higher than requested due to the implementation rounding up to ensure all memory pages allocated to the zone are utilised to capacity. The limit applies to the total number of items in the zone, which includes allocated items, free items and free items in the per-cpu caches. On systems with more than one CPU it may not be possible to allocate the specified number of items even when there is no shortage of memory, because all of the remaining free items may be in the caches of the other CPUs when the limit is hit. .Pp The .Fn uma_zone_set_maxcache function limits the number of free items which may be cached in the zone. This limit applies to both the per-CPU caches and the cache of free buckets. .Pp The .Fn uma_zone_get_max function returns the effective upper limit number of items for a zone. .Pp The .Fn uma_zone_get_cur function returns an approximation of the number of items currently allocated from the zone. The returned value is approximate because appropriate synchronisation to determine an exact value is not performed by the implementation. This ensures low overhead at the expense of potentially stale data being used in the calculation. .Pp The .Fn uma_zone_set_warning function sets a warning that will be printed on the system console when the given zone becomes full and fails to allocate an item. The warning will be printed no more often than every five minutes. Warnings can be turned off globally by setting the .Va vm.zone_warnings sysctl tunable to .Va 0 . .Pp The .Fn uma_zone_set_maxaction function sets a function that will be called when the given zone becomes full and fails to allocate an item. The function will be called with the zone locked. Also, the function that called the allocation function may have held additional locks. Therefore, this function should do very little work (similar to a signal handler). .Pp The .Fn uma_zone_set_smr function associates an existing .Xr smr 9 structure with a UMA zone. The effect is similar to creating a zone with the .Dv UMA_ZONE_SMR flag, except that a new SMR structure is not created. This function must be called before any allocations from the zone are performed. .Pp The .Fn SYSCTL_UMA_MAX parent nbr name access zone descr macro declares a static .Xr sysctl 9 oid that exports the effective upper limit number of items for a zone. The .Fa zone argument should be a pointer to .Vt uma_zone_t . A read of the oid returns value obtained through .Fn uma_zone_get_max . A write to the oid sets new value via .Fn uma_zone_set_max . The .Fn SYSCTL_ADD_UMA_MAX ctx parent nbr name access zone descr macro is provided to create this type of oid dynamically. .Pp The .Fn SYSCTL_UMA_CUR parent nbr name access zone descr macro declares a static read-only .Xr sysctl 9 oid that exports the approximate current occupancy of the zone. The .Fa zone argument should be a pointer to .Vt uma_zone_t . A read of the oid returns value obtained through .Fn uma_zone_get_cur . The .Fn SYSCTL_ADD_UMA_CUR ctx parent nbr name zone descr macro is provided to create this type of oid dynamically. .Sh IMPLEMENTATION NOTES The memory that these allocation calls return is not executable. The .Fn uma_zalloc function does not support the .Dv M_EXEC flag to allocate executable memory. Not all platforms enforce a distinction between executable and non-executable memory. .Sh SEE ALSO .Xr numa 4 , .Xr vmstat 8 , .Xr malloc 9 , .Xr smr 9 .Rs .%A Jeff Bonwick .%T "The Slab Allocator: An Object-Caching Kernel Memory Allocator" .%D 1994 .Re .Sh HISTORY The zone allocator first appeared in .Fx 3.0 . It was radically changed in .Fx 5.0 to function as a slab allocator. .Sh AUTHORS .An -nosplit The zone allocator was written by .An John S. Dyson . The zone allocator was rewritten in large parts by .An Jeff Roberson Aq Mt jeff@FreeBSD.org to function as a slab allocator. .Pp This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . Changes for UMA by .An Jeroen Ruigrok van der Werven Aq Mt asmodai@FreeBSD.org . diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c index 5c812353f989..856e5aca07d6 100644 --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -1,2279 +1,2279 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * - * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2000 Dag-Erling Smørgrav * Copyright (c) 1999 Pierre Beyssac * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94 */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #include #endif /* __i386__ || __amd64__ */ #include #include #include #include #include #include #include #include /* * Various conversion macros */ #define T2J(x) ((long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to jiffies */ #define T2CS(x) ((unsigned long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to centiseconds */ #define T2S(x) ((x) / (stathz ? stathz : hz)) /* ticks to seconds */ #define B2K(x) ((x) >> 10) /* bytes to kbytes */ #define B2P(x) ((x) >> PAGE_SHIFT) /* bytes to pages */ #define P2B(x) ((x) << PAGE_SHIFT) /* pages to bytes */ #define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* pages to kbytes */ #define TV2J(x) ((x)->tv_sec * 100UL + (x)->tv_usec / 10000) /** * @brief Mapping of ki_stat in struct kinfo_proc to the linux state * * The linux procfs state field displays one of the characters RSDZTW to * denote running, sleeping in an interruptible wait, waiting in an * uninterruptible disk sleep, a zombie process, process is being traced * or stopped, or process is paging respectively. * * Our struct kinfo_proc contains the variable ki_stat which contains a * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK. * * This character array is used with ki_stati-1 as an index and tries to * map our states to suitable linux states. */ static char linux_state[] = "RRSTZDD"; /* * Filler function for proc/meminfo */ static int linprocfs_domeminfo(PFS_FILL_ARGS) { unsigned long memtotal; /* total memory in bytes */ unsigned long memfree; /* free memory in bytes */ unsigned long cached; /* page cache */ unsigned long buffers; /* buffer cache */ unsigned long long swaptotal; /* total swap space in bytes */ unsigned long long swapused; /* used swap space in bytes */ unsigned long long swapfree; /* free swap space in bytes */ size_t sz; int error, i, j; memtotal = physmem * PAGE_SIZE; memfree = (unsigned long)vm_free_count() * PAGE_SIZE; swap_pager_status(&i, &j); swaptotal = (unsigned long long)i * PAGE_SIZE; swapused = (unsigned long long)j * PAGE_SIZE; swapfree = swaptotal - swapused; /* * This value may exclude wired pages, but we have no good way of * accounting for that. */ cached = (vm_active_count() + vm_inactive_count() + vm_laundry_count()) * PAGE_SIZE; sz = sizeof(buffers); error = kernel_sysctlbyname(curthread, "vfs.bufspace", &buffers, &sz, NULL, 0, 0, 0); if (error != 0) buffers = 0; sbuf_printf(sb, "MemTotal: %9lu kB\n" "MemFree: %9lu kB\n" "Buffers: %9lu kB\n" "Cached: %9lu kB\n" "SwapTotal:%9llu kB\n" "SwapFree: %9llu kB\n", B2K(memtotal), B2K(memfree), B2K(buffers), B2K(cached), B2K(swaptotal), B2K(swapfree)); return (0); } #if defined(__i386__) || defined(__amd64__) /* * Filler function for proc/cpuinfo (i386 & amd64 version) */ static int linprocfs_docpuinfo(PFS_FILL_ARGS) { int hw_model[2]; char model[128]; uint64_t freq; size_t size; u_int cache_size[4]; u_int regs[4] = { 0 }; int fqmhz, fqkhz; int i, j; /* * We default the flags to include all non-conflicting flags, * and the Intel versions of conflicting flags. */ static char *cpu_feature_names[] = { /* 0 */ "fpu", "vme", "de", "pse", /* 4 */ "tsc", "msr", "pae", "mce", /* 8 */ "cx8", "apic", "", "sep", /* 12 */ "mtrr", "pge", "mca", "cmov", /* 16 */ "pat", "pse36", "pn", "clflush", /* 20 */ "", "dts", "acpi", "mmx", /* 24 */ "fxsr", "sse", "sse2", "ss", /* 28 */ "ht", "tm", "ia64", "pbe" }; static char *amd_feature_names[] = { /* 0 */ "", "", "", "", /* 4 */ "", "", "", "", /* 8 */ "", "", "", "syscall", /* 12 */ "", "", "", "", /* 16 */ "", "", "", "mp", /* 20 */ "nx", "", "mmxext", "", /* 24 */ "", "fxsr_opt", "pdpe1gb", "rdtscp", /* 28 */ "", "lm", "3dnowext", "3dnow" }; static char *cpu_feature2_names[] = { /* 0 */ "pni", "pclmulqdq", "dtes64", "monitor", /* 4 */ "ds_cpl", "vmx", "smx", "est", /* 8 */ "tm2", "ssse3", "cid", "sdbg", /* 12 */ "fma", "cx16", "xtpr", "pdcm", /* 16 */ "", "pcid", "dca", "sse4_1", /* 20 */ "sse4_2", "x2apic", "movbe", "popcnt", /* 24 */ "tsc_deadline_timer", "aes", "xsave", "", /* 28 */ "avx", "f16c", "rdrand", "hypervisor" }; static char *amd_feature2_names[] = { /* 0 */ "lahf_lm", "cmp_legacy", "svm", "extapic", /* 4 */ "cr8_legacy", "abm", "sse4a", "misalignsse", /* 8 */ "3dnowprefetch", "osvw", "ibs", "xop", /* 12 */ "skinit", "wdt", "", "lwp", /* 16 */ "fma4", "tce", "", "nodeid_msr", /* 20 */ "", "tbm", "topoext", "perfctr_core", /* 24 */ "perfctr_nb", "", "bpext", "ptsc", /* 28 */ "perfctr_llc", "mwaitx", "", "" }; static char *cpu_stdext_feature_names[] = { /* 0 */ "fsgsbase", "tsc_adjust", "sgx", "bmi1", /* 4 */ "hle", "avx2", "", "smep", /* 8 */ "bmi2", "erms", "invpcid", "rtm", /* 12 */ "cqm", "", "mpx", "rdt_a", /* 16 */ "avx512f", "avx512dq", "rdseed", "adx", /* 20 */ "smap", "avx512ifma", "", "clflushopt", /* 24 */ "clwb", "intel_pt", "avx512pf", "avx512er", /* 28 */ "avx512cd", "sha_ni", "avx512bw", "avx512vl" }; static char *cpu_stdext_feature2_names[] = { /* 0 */ "prefetchwt1", "avx512vbmi", "umip", "pku", /* 4 */ "ospke", "waitpkg", "avx512_vbmi2", "", /* 8 */ "gfni", "vaes", "vpclmulqdq", "avx512_vnni", /* 12 */ "avx512_bitalg", "", "avx512_vpopcntdq", "", /* 16 */ "", "", "", "", /* 20 */ "", "", "rdpid", "", /* 24 */ "", "cldemote", "", "movdiri", /* 28 */ "movdir64b", "enqcmd", "sgx_lc", "" }; static char *cpu_stdext_feature3_names[] = { /* 0 */ "", "", "avx512_4vnniw", "avx512_4fmaps", /* 4 */ "fsrm", "", "", "", /* 8 */ "avx512_vp2intersect", "", "md_clear", "", /* 12 */ "", "", "", "", /* 16 */ "", "", "pconfig", "", /* 20 */ "", "", "", "", /* 24 */ "", "", "ibrs", "stibp", /* 28 */ "flush_l1d", "arch_capabilities", "core_capabilities", "ssbd" }; static char *cpu_stdext_feature_l1_names[] = { /* 0 */ "xsaveopt", "xsavec", "xgetbv1", "xsaves", /* 4 */ "xfd" }; static char *power_flags[] = { "ts", "fid", "vid", "ttp", "tm", "stc", "100mhzsteps", "hwpstate", "", "cpb", "eff_freq_ro", "proc_feedback", "acc_power", }; hw_model[0] = CTL_HW; hw_model[1] = HW_MODEL; model[0] = '\0'; size = sizeof(model); if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0) strcpy(model, "unknown"); #ifdef __i386__ switch (cpu_vendor_id) { case CPU_VENDOR_AMD: if (cpu_class < CPUCLASS_686) cpu_feature_names[16] = "fcmov"; break; case CPU_VENDOR_CYRIX: cpu_feature_names[24] = "cxmmx"; break; } #endif if (cpu_exthigh >= 0x80000006) do_cpuid(0x80000006, cache_size); else memset(cache_size, 0, sizeof(cache_size)); for (i = 0; i < mp_ncpus; ++i) { fqmhz = 0; fqkhz = 0; freq = atomic_load_acq_64(&tsc_freq); if (freq != 0) { fqmhz = (freq + 4999) / 1000000; fqkhz = ((freq + 4999) / 10000) % 100; } sbuf_printf(sb, "processor\t: %d\n" "vendor_id\t: %.20s\n" "cpu family\t: %u\n" "model\t\t: %u\n" "model name\t: %s\n" "stepping\t: %u\n" "cpu MHz\t\t: %d.%02d\n" "cache size\t: %d KB\n" "physical id\t: %d\n" "siblings\t: %d\n" "core id\t\t: %d\n" "cpu cores\t: %d\n" "apicid\t\t: %d\n" "initial apicid\t: %d\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid level\t: %d\n" "wp\t\t: %s\n", i, cpu_vendor, CPUID_TO_FAMILY(cpu_id), CPUID_TO_MODEL(cpu_id), model, cpu_id & CPUID_STEPPING, fqmhz, fqkhz, (cache_size[2] >> 16), 0, mp_ncpus, i, mp_ncpus, i, i, /*cpu_id & CPUID_LOCAL_APIC_ID ??*/ (cpu_feature & CPUID_FPU) ? "yes" : "no", "yes", CPUID_TO_FAMILY(cpu_id), "yes"); sbuf_cat(sb, "flags\t\t:"); for (j = 0; j < nitems(cpu_feature_names); j++) if (cpu_feature & (1 << j) && cpu_feature_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_feature_names[j]); for (j = 0; j < nitems(amd_feature_names); j++) if (amd_feature & (1 << j) && amd_feature_names[j][0] != '\0') sbuf_printf(sb, " %s", amd_feature_names[j]); for (j = 0; j < nitems(cpu_feature2_names); j++) if (cpu_feature2 & (1 << j) && cpu_feature2_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_feature2_names[j]); for (j = 0; j < nitems(amd_feature2_names); j++) if (amd_feature2 & (1 << j) && amd_feature2_names[j][0] != '\0') sbuf_printf(sb, " %s", amd_feature2_names[j]); for (j = 0; j < nitems(cpu_stdext_feature_names); j++) if (cpu_stdext_feature & (1 << j) && cpu_stdext_feature_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_stdext_feature_names[j]); if (tsc_is_invariant) sbuf_cat(sb, " constant_tsc"); for (j = 0; j < nitems(cpu_stdext_feature2_names); j++) if (cpu_stdext_feature2 & (1 << j) && cpu_stdext_feature2_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_stdext_feature2_names[j]); for (j = 0; j < nitems(cpu_stdext_feature3_names); j++) if (cpu_stdext_feature3 & (1 << j) && cpu_stdext_feature3_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_stdext_feature3_names[j]); if ((cpu_feature2 & CPUID2_XSAVE) != 0) { cpuid_count(0xd, 0x1, regs); for (j = 0; j < nitems(cpu_stdext_feature_l1_names); j++) if (regs[0] & (1 << j) && cpu_stdext_feature_l1_names[j][0] != '\0') sbuf_printf(sb, " %s", cpu_stdext_feature_l1_names[j]); } sbuf_cat(sb, "\n"); sbuf_printf(sb, "bugs\t\t: %s\n" "bogomips\t: %d.%02d\n" "clflush size\t: %d\n" "cache_alignment\t: %d\n" "address sizes\t: %d bits physical, %d bits virtual\n", #if defined(I586_CPU) && !defined(NO_F00F_HACK) (has_f00f_bug) ? "Intel F00F" : "", #else "", #endif fqmhz * 2, fqkhz, cpu_clflush_line_size, cpu_clflush_line_size, cpu_maxphyaddr, (cpu_maxphyaddr > 32) ? 48 : 0); sbuf_cat(sb, "power management: "); for (j = 0; j < nitems(power_flags); j++) if (amd_pminfo & (1 << j)) sbuf_printf(sb, " %s", power_flags[j]); sbuf_cat(sb, "\n\n"); /* XXX per-cpu vendor / class / model / id? */ } sbuf_cat(sb, "\n"); return (0); } #else /* ARM64TODO: implement non-stubbed linprocfs_docpuinfo */ static int linprocfs_docpuinfo(PFS_FILL_ARGS) { int i; for (i = 0; i < mp_ncpus; ++i) { sbuf_printf(sb, "processor\t: %d\n" "BogoMIPS\t: %d.%02d\n", i, 0, 0); sbuf_cat(sb, "Features\t: "); sbuf_cat(sb, "\n"); sbuf_printf(sb, "CPU implementer\t: \n" "CPU architecture: \n" "CPU variant\t: 0x%x\n" "CPU part\t: 0x%x\n" "CPU revision\t: %d\n", 0, 0, 0); sbuf_cat(sb, "\n"); } return (0); } #endif /* __i386__ || __amd64__ */ static const char *path_slash_sys = "/sys"; static const char *fstype_sysfs = "sysfs"; static int _mtab_helper(const struct pfs_node *pn, const struct statfs *sp, const char **mntfrom, const char **mntto, const char **fstype) { /* determine device name */ *mntfrom = sp->f_mntfromname; /* determine mount point */ *mntto = sp->f_mntonname; /* determine fs type */ *fstype = sp->f_fstypename; if (strcmp(*fstype, pn->pn_info->pi_name) == 0) *mntfrom = *fstype = "proc"; else if (strcmp(*fstype, "procfs") == 0) return (ECANCELED); if (strcmp(*fstype, "autofs") == 0) { /* * FreeBSD uses eg "map -hosts", whereas Linux * expects just "-hosts". */ if (strncmp(*mntfrom, "map ", 4) == 0) *mntfrom += 4; } if (strcmp(*fstype, "linsysfs") == 0) { *mntfrom = path_slash_sys; *fstype = fstype_sysfs; } else { /* For Linux msdosfs is called vfat */ if (strcmp(*fstype, "msdosfs") == 0) *fstype = "vfat"; } return (0); } static void _sbuf_mntoptions_helper(struct sbuf *sb, uint64_t f_flags) { sbuf_cat(sb, (f_flags & MNT_RDONLY) ? "ro" : "rw"); #define ADD_OPTION(opt, name) \ if (f_flags & (opt)) sbuf_cat(sb, "," name); ADD_OPTION(MNT_SYNCHRONOUS, "sync"); ADD_OPTION(MNT_NOEXEC, "noexec"); ADD_OPTION(MNT_NOSUID, "nosuid"); ADD_OPTION(MNT_UNION, "union"); ADD_OPTION(MNT_ASYNC, "async"); ADD_OPTION(MNT_SUIDDIR, "suiddir"); ADD_OPTION(MNT_NOSYMFOLLOW, "nosymfollow"); ADD_OPTION(MNT_NOATIME, "noatime"); #undef ADD_OPTION } /* * Filler function for proc/mtab and proc//mounts. * * /proc/mtab doesn't exist in Linux' procfs, but is included here so * users can symlink /compat/linux/etc/mtab to /proc/mtab */ static int linprocfs_domtab(PFS_FILL_ARGS) { const char *mntto, *mntfrom, *fstype; char *dlep, *flep; struct vnode *vp; struct pwd *pwd; size_t lep_len; int error; struct statfs *buf, *sp; size_t count; /* * Resolve emulation tree prefix */ flep = NULL; pwd = pwd_hold(td); vp = pwd->pwd_adir; error = vn_fullpath_global(vp, &dlep, &flep); pwd_drop(pwd); if (error != 0) return (error); lep_len = strlen(dlep); buf = NULL; error = kern_getfsstat(td, &buf, SIZE_T_MAX, &count, UIO_SYSSPACE, MNT_WAIT); if (error != 0) { free(buf, M_TEMP); free(flep, M_TEMP); return (error); } for (sp = buf; count > 0; sp++, count--) { error = _mtab_helper(pn, sp, &mntfrom, &mntto, &fstype); if (error != 0) { MPASS(error == ECANCELED); continue; } /* determine mount point */ if (strncmp(mntto, dlep, lep_len) == 0 && mntto[lep_len] == '/') mntto += lep_len; sbuf_printf(sb, "%s %s %s ", mntfrom, mntto, fstype); _sbuf_mntoptions_helper(sb, sp->f_flags); /* a real Linux mtab will also show NFS options */ sbuf_printf(sb, " 0 0\n"); } free(buf, M_TEMP); free(flep, M_TEMP); return (error); } static int linprocfs_doprocmountinfo(PFS_FILL_ARGS) { const char *mntfrom, *mntto, *fstype; char *dlep, *flep; struct statfs *buf, *sp; size_t count, lep_len; struct vnode *vp; struct pwd *pwd; int error; /* * Resolve emulation tree prefix */ flep = NULL; pwd = pwd_hold(td); vp = pwd->pwd_adir; error = vn_fullpath_global(vp, &dlep, &flep); pwd_drop(pwd); if (error != 0) return (error); lep_len = strlen(dlep); buf = NULL; error = kern_getfsstat(td, &buf, SIZE_T_MAX, &count, UIO_SYSSPACE, MNT_WAIT); if (error != 0) goto out; for (sp = buf; count > 0; sp++, count--) { error = _mtab_helper(pn, sp, &mntfrom, &mntto, &fstype); if (error != 0) { MPASS(error == ECANCELED); continue; } if (strncmp(mntto, dlep, lep_len) == 0 && mntto[lep_len] == '/') mntto += lep_len; #if 0 /* * If the prefix is a chroot, and this mountpoint is not under * the prefix, we should skip it. Leave it for now for * consistency with procmtab above. */ else continue; #endif /* * (1) mount id * * (2) parent mount id -- we don't have this cheaply, so * provide a dummy value * * (3) major:minor -- ditto * * (4) root filesystem mount -- probably a namespaces thing * * (5) mountto path */ sbuf_printf(sb, "%u 0 0:0 / %s ", sp->f_fsid.val[0] ^ sp->f_fsid.val[1], mntto); /* (6) mount options */ _sbuf_mntoptions_helper(sb, sp->f_flags); /* * (7) zero or more optional fields -- again, namespace related * * (8) End of variable length fields separator ("-") * * (9) fstype * * (10) mount from * * (11) "superblock" options -- like (6), but different * semantics in Linux */ sbuf_printf(sb, " - %s %s %s\n", fstype, mntfrom, (sp->f_flags & MNT_RDONLY) ? "ro" : "rw"); } error = 0; out: free(buf, M_TEMP); free(flep, M_TEMP); return (error); } /* * Filler function for proc/partitions */ static int linprocfs_dopartitions(PFS_FILL_ARGS) { struct g_class *cp; struct g_geom *gp; struct g_provider *pp; int major, minor; g_topology_lock(); sbuf_printf(sb, "major minor #blocks name rio rmerge rsect " "ruse wio wmerge wsect wuse running use aveq\n"); LIST_FOREACH(cp, &g_classes, class) { if (strcmp(cp->name, "DISK") == 0 || strcmp(cp->name, "PART") == 0) LIST_FOREACH(gp, &cp->geom, geom) { LIST_FOREACH(pp, &gp->provider, provider) { if (linux_driver_get_major_minor( pp->name, &major, &minor) != 0) { major = 0; minor = 0; } sbuf_printf(sb, "%d %d %lld %s " "%d %d %d %d %d " "%d %d %d %d %d %d\n", major, minor, (long long)pp->mediasize, pp->name, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } } g_topology_unlock(); return (0); } /* * Filler function for proc/stat * * Output depends on kernel version: * * v2.5.40 <= * user nice system idle * v2.5.41 * user nice system idle iowait * v2.6.11 * user nice system idle iowait irq softirq steal * v2.6.24 * user nice system idle iowait irq softirq steal guest * v2.6.33 >= * user nice system idle iowait irq softirq steal guest guest_nice */ static int linprocfs_dostat(PFS_FILL_ARGS) { struct pcpu *pcpu; long cp_time[CPUSTATES]; long *cp; struct timeval boottime; int i; char *zero_pad; bool has_intr = true; if (linux_kernver(td) >= LINUX_KERNVER(2,6,33)) { zero_pad = " 0 0 0 0\n"; } else if (linux_kernver(td) >= LINUX_KERNVER(2,6,24)) { zero_pad = " 0 0 0\n"; } else if (linux_kernver(td) >= LINUX_KERNVER(2,6,11)) { zero_pad = " 0 0\n"; } else if (linux_kernver(td) >= LINUX_KERNVER(2,5,41)) { has_intr = false; zero_pad = " 0\n"; } else { has_intr = false; zero_pad = "\n"; } read_cpu_time(cp_time); getboottime(&boottime); /* Parameters common to all versions */ sbuf_printf(sb, "cpu %lu %lu %lu %lu", T2J(cp_time[CP_USER]), T2J(cp_time[CP_NICE]), T2J(cp_time[CP_SYS]), T2J(cp_time[CP_IDLE])); /* Print interrupt stats if available */ if (has_intr) { sbuf_printf(sb, " 0 %lu", T2J(cp_time[CP_INTR])); } /* Pad out remaining fields depending on version */ sbuf_printf(sb, "%s", zero_pad); CPU_FOREACH(i) { pcpu = pcpu_find(i); cp = pcpu->pc_cp_time; sbuf_printf(sb, "cpu%d %lu %lu %lu %lu", i, T2J(cp[CP_USER]), T2J(cp[CP_NICE]), T2J(cp[CP_SYS]), T2J(cp[CP_IDLE])); if (has_intr) { sbuf_printf(sb, " 0 %lu", T2J(cp[CP_INTR])); } sbuf_printf(sb, "%s", zero_pad); } sbuf_printf(sb, "disk 0 0 0 0\n" "page %ju %ju\n" "swap %ju %ju\n" "intr %ju\n" "ctxt %ju\n" "btime %lld\n", (uintmax_t)VM_CNT_FETCH(v_vnodepgsin), (uintmax_t)VM_CNT_FETCH(v_vnodepgsout), (uintmax_t)VM_CNT_FETCH(v_swappgsin), (uintmax_t)VM_CNT_FETCH(v_swappgsout), (uintmax_t)VM_CNT_FETCH(v_intr), (uintmax_t)VM_CNT_FETCH(v_swtch), (long long)boottime.tv_sec); return (0); } static int linprocfs_doswaps(PFS_FILL_ARGS) { struct xswdev xsw; uintmax_t total, used; int n; char devname[SPECNAMELEN + 1]; sbuf_printf(sb, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n"); for (n = 0; ; n++) { if (swap_dev_info(n, &xsw, devname, sizeof(devname)) != 0) break; total = (uintmax_t)xsw.xsw_nblks * PAGE_SIZE / 1024; used = (uintmax_t)xsw.xsw_used * PAGE_SIZE / 1024; /* * The space and not tab after the device name is on * purpose. Linux does so. */ sbuf_printf(sb, "/dev/%-34s unknown\t\t%jd\t%jd\t-1\n", devname, total, used); } return (0); } /* * Filler function for proc/uptime */ static int linprocfs_douptime(PFS_FILL_ARGS) { long cp_time[CPUSTATES]; struct timeval tv; getmicrouptime(&tv); read_cpu_time(cp_time); sbuf_printf(sb, "%lld.%02ld %ld.%02lu\n", (long long)tv.tv_sec, tv.tv_usec / 10000, T2S(cp_time[CP_IDLE] / mp_ncpus), T2CS(cp_time[CP_IDLE] / mp_ncpus) % 100); return (0); } /* * Get OS build date */ static void linprocfs_osbuild(struct thread *td, struct sbuf *sb) { #if 0 char osbuild[256]; char *cp1, *cp2; strncpy(osbuild, version, 256); osbuild[255] = '\0'; cp1 = strstr(osbuild, "\n"); cp2 = strstr(osbuild, ":"); if (cp1 && cp2) { *cp1 = *cp2 = '\0'; cp1 = strstr(osbuild, "#"); } else cp1 = NULL; if (cp1) sbuf_printf(sb, "%s%s", cp1, cp2 + 1); else #endif sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977"); } /* * Get OS builder */ static void linprocfs_osbuilder(struct thread *td, struct sbuf *sb) { #if 0 char builder[256]; char *cp; cp = strstr(version, "\n "); if (cp) { strncpy(builder, cp + 5, 256); builder[255] = '\0'; cp = strstr(builder, ":"); if (cp) *cp = '\0'; } if (cp) sbuf_cat(sb, builder); else #endif sbuf_cat(sb, "des@freebsd.org"); } /* * Filler function for proc/version */ static int linprocfs_doversion(PFS_FILL_ARGS) { char osname[LINUX_MAX_UTSNAME]; char osrelease[LINUX_MAX_UTSNAME]; linux_get_osname(td, osname); linux_get_osrelease(td, osrelease); sbuf_printf(sb, "%s version %s (", osname, osrelease); linprocfs_osbuilder(td, sb); sbuf_cat(sb, ") (gcc version " __VERSION__ ") "); linprocfs_osbuild(td, sb); sbuf_cat(sb, "\n"); return (0); } /* * Filler function for proc/loadavg */ static int linprocfs_doloadavg(PFS_FILL_ARGS) { sbuf_printf(sb, "%d.%02d %d.%02d %d.%02d %d/%d %d\n", (int)(averunnable.ldavg[0] / averunnable.fscale), (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100), (int)(averunnable.ldavg[1] / averunnable.fscale), (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100), (int)(averunnable.ldavg[2] / averunnable.fscale), (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100), 1, /* number of running tasks */ nprocs, /* number of tasks */ lastpid /* the last pid */ ); return (0); } static int linprocfs_get_tty_nr(struct proc *p) { struct session *sp; const char *ttyname; int error, major, minor, nr; PROC_LOCK_ASSERT(p, MA_OWNED); sx_assert(&proctree_lock, SX_LOCKED); if ((p->p_flag & P_CONTROLT) == 0) return (-1); sp = p->p_pgrp->pg_session; if (sp == NULL) return (-1); ttyname = devtoname(sp->s_ttyp->t_dev); error = linux_driver_get_major_minor(ttyname, &major, &minor); if (error != 0) return (-1); nr = makedev(major, minor); return (nr); } /* * Filler function for proc/pid/stat */ static int linprocfs_doprocstat(PFS_FILL_ARGS) { struct kinfo_proc kp; struct timeval boottime; char state; static int ratelimit = 0; int tty_nr; vm_offset_t startcode, startdata; getboottime(&boottime); sx_slock(&proctree_lock); PROC_LOCK(p); fill_kinfo_proc(p, &kp); tty_nr = linprocfs_get_tty_nr(p); sx_sunlock(&proctree_lock); if (p->p_vmspace) { startcode = (vm_offset_t)p->p_vmspace->vm_taddr; startdata = (vm_offset_t)p->p_vmspace->vm_daddr; } else { startcode = 0; startdata = 0; } sbuf_printf(sb, "%d", p->p_pid); #define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg) PS_ADD("comm", "(%s)", p->p_comm); if (kp.ki_stat > sizeof(linux_state)) { state = 'R'; if (ratelimit == 0) { printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n", kp.ki_stat, sizeof(linux_state)); ++ratelimit; } } else state = linux_state[kp.ki_stat - 1]; PS_ADD("state", "%c", state); PS_ADD("ppid", "%d", p->p_pptr ? p->p_pptr->p_pid : 0); PS_ADD("pgrp", "%d", p->p_pgid); PS_ADD("session", "%d", p->p_session->s_sid); PROC_UNLOCK(p); PS_ADD("tty", "%d", tty_nr); PS_ADD("tpgid", "%d", kp.ki_tpgid); PS_ADD("flags", "%u", 0); /* XXX */ PS_ADD("minflt", "%lu", kp.ki_rusage.ru_minflt); PS_ADD("cminflt", "%lu", kp.ki_rusage_ch.ru_minflt); PS_ADD("majflt", "%lu", kp.ki_rusage.ru_majflt); PS_ADD("cmajflt", "%lu", kp.ki_rusage_ch.ru_majflt); PS_ADD("utime", "%ld", TV2J(&kp.ki_rusage.ru_utime)); PS_ADD("stime", "%ld", TV2J(&kp.ki_rusage.ru_stime)); PS_ADD("cutime", "%ld", TV2J(&kp.ki_rusage_ch.ru_utime)); PS_ADD("cstime", "%ld", TV2J(&kp.ki_rusage_ch.ru_stime)); PS_ADD("priority", "%d", kp.ki_pri.pri_user); PS_ADD("nice", "%d", kp.ki_nice); /* 19 (nicest) to -19 */ PS_ADD("0", "%d", 0); /* removed field */ PS_ADD("itrealvalue", "%d", 0); /* XXX */ PS_ADD("starttime", "%lu", TV2J(&kp.ki_start) - TV2J(&boottime)); PS_ADD("vsize", "%ju", (uintmax_t)kp.ki_size); PS_ADD("rss", "%ju", (uintmax_t)kp.ki_rssize); PS_ADD("rlim", "%lu", kp.ki_rusage.ru_maxrss); PS_ADD("startcode", "%ju", (uintmax_t)startcode); PS_ADD("endcode", "%ju", (uintmax_t)startdata); PS_ADD("startstack", "%u", 0); /* XXX */ PS_ADD("kstkesp", "%u", 0); /* XXX */ PS_ADD("kstkeip", "%u", 0); /* XXX */ PS_ADD("signal", "%u", 0); /* XXX */ PS_ADD("blocked", "%u", 0); /* XXX */ PS_ADD("sigignore", "%u", 0); /* XXX */ PS_ADD("sigcatch", "%u", 0); /* XXX */ PS_ADD("wchan", "%u", 0); /* XXX */ PS_ADD("nswap", "%lu", kp.ki_rusage.ru_nswap); PS_ADD("cnswap", "%lu", kp.ki_rusage_ch.ru_nswap); PS_ADD("exitsignal", "%d", 0); /* XXX */ PS_ADD("processor", "%u", kp.ki_lastcpu); PS_ADD("rt_priority", "%u", 0); /* XXX */ /* >= 2.5.19 */ PS_ADD("policy", "%u", kp.ki_pri.pri_class); /* >= 2.5.19 */ #undef PS_ADD sbuf_putc(sb, '\n'); return (0); } /* * Filler function for proc/pid/statm */ static int linprocfs_doprocstatm(PFS_FILL_ARGS) { struct kinfo_proc kp; segsz_t lsize; sx_slock(&proctree_lock); PROC_LOCK(p); fill_kinfo_proc(p, &kp); PROC_UNLOCK(p); sx_sunlock(&proctree_lock); /* * See comments in linprocfs_doprocstatus() regarding the * computation of lsize. */ /* size resident share trs drs lrs dt */ sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size)); sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize); sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */ sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_tsize); sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize)); lsize = B2P(kp.ki_size) - kp.ki_dsize - kp.ki_ssize - kp.ki_tsize - 1; sbuf_printf(sb, "%ju ", (uintmax_t)lsize); sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */ return (0); } /* * Filler function for proc/pid/status */ static int linprocfs_doprocstatus(PFS_FILL_ARGS) { struct kinfo_proc kp; char *state; segsz_t lsize; struct thread *td2; struct sigacts *ps; l_sigset_t siglist, sigignore, sigcatch; int i; sx_slock(&proctree_lock); PROC_LOCK(p); td2 = FIRST_THREAD_IN_PROC(p); if (P_SHOULDSTOP(p)) { state = "T (stopped)"; } else { switch(p->p_state) { case PRS_NEW: state = "I (idle)"; break; case PRS_NORMAL: if (p->p_flag & P_WEXIT) { state = "X (exiting)"; break; } switch(TD_GET_STATE(td2)) { case TDS_INHIBITED: state = "S (sleeping)"; break; case TDS_RUNQ: case TDS_RUNNING: state = "R (running)"; break; default: state = "? (unknown)"; break; } break; case PRS_ZOMBIE: state = "Z (zombie)"; break; default: state = "? (unknown)"; break; } } fill_kinfo_proc(p, &kp); sx_sunlock(&proctree_lock); sbuf_printf(sb, "Name:\t%s\n", p->p_comm); /* XXX escape */ sbuf_printf(sb, "State:\t%s\n", state); /* * Credentials */ sbuf_printf(sb, "Tgid:\t%d\n", p->p_pid); sbuf_printf(sb, "Pid:\t%d\n", p->p_pid); sbuf_printf(sb, "PPid:\t%d\n", kp.ki_ppid ); sbuf_printf(sb, "TracerPid:\t%d\n", kp.ki_tracer ); sbuf_printf(sb, "Uid:\t%d\t%d\t%d\t%d\n", p->p_ucred->cr_ruid, p->p_ucred->cr_uid, p->p_ucred->cr_svuid, /* FreeBSD doesn't have fsuid */ p->p_ucred->cr_uid); sbuf_printf(sb, "Gid:\t%d\t%d\t%d\t%d\n", p->p_ucred->cr_rgid, p->p_ucred->cr_gid, p->p_ucred->cr_svgid, /* FreeBSD doesn't have fsgid */ p->p_ucred->cr_gid); sbuf_cat(sb, "Groups:\t"); for (i = 0; i < p->p_ucred->cr_ngroups; i++) sbuf_printf(sb, "%d ", p->p_ucred->cr_groups[i]); PROC_UNLOCK(p); sbuf_putc(sb, '\n'); /* * Memory * * While our approximation of VmLib may not be accurate (I * don't know of a simple way to verify it, and I'm not sure * it has much meaning anyway), I believe it's good enough. * * The same code that could (I think) accurately compute VmLib * could also compute VmLck, but I don't really care enough to * implement it. Submissions are welcome. */ sbuf_printf(sb, "VmSize:\t%8ju kB\n", B2K((uintmax_t)kp.ki_size)); sbuf_printf(sb, "VmLck:\t%8u kB\n", P2K(0)); /* XXX */ sbuf_printf(sb, "VmRSS:\t%8ju kB\n", P2K((uintmax_t)kp.ki_rssize)); sbuf_printf(sb, "VmData:\t%8ju kB\n", P2K((uintmax_t)kp.ki_dsize)); sbuf_printf(sb, "VmStk:\t%8ju kB\n", P2K((uintmax_t)kp.ki_ssize)); sbuf_printf(sb, "VmExe:\t%8ju kB\n", P2K((uintmax_t)kp.ki_tsize)); lsize = B2P(kp.ki_size) - kp.ki_dsize - kp.ki_ssize - kp.ki_tsize - 1; sbuf_printf(sb, "VmLib:\t%8ju kB\n", P2K((uintmax_t)lsize)); /* * Signal masks */ PROC_LOCK(p); bsd_to_linux_sigset(&p->p_siglist, &siglist); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); bsd_to_linux_sigset(&ps->ps_sigignore, &sigignore); bsd_to_linux_sigset(&ps->ps_sigcatch, &sigcatch); mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); sbuf_printf(sb, "SigPnd:\t%016jx\n", siglist.__mask); /* * XXX. SigBlk - target thread's signal mask, td_sigmask. * To implement SigBlk pseudofs should support proc/tid dir entries. */ sbuf_printf(sb, "SigBlk:\t%016x\n", 0); sbuf_printf(sb, "SigIgn:\t%016jx\n", sigignore.__mask); sbuf_printf(sb, "SigCgt:\t%016jx\n", sigcatch.__mask); /* * Linux also prints the capability masks, but we don't have * capabilities yet, and when we do get them they're likely to * be meaningless to Linux programs, so we lie. XXX */ sbuf_printf(sb, "CapInh:\t%016x\n", 0); sbuf_printf(sb, "CapPrm:\t%016x\n", 0); sbuf_printf(sb, "CapEff:\t%016x\n", 0); return (0); } /* * Filler function for proc/pid/cwd */ static int linprocfs_doproccwd(PFS_FILL_ARGS) { struct pwd *pwd; char *fullpath = "unknown"; char *freepath = NULL; pwd = pwd_hold_proc(p); vn_fullpath(pwd->pwd_cdir, &fullpath, &freepath); sbuf_printf(sb, "%s", fullpath); if (freepath) free(freepath, M_TEMP); pwd_drop(pwd); return (0); } /* * Filler function for proc/pid/root */ static int linprocfs_doprocroot(PFS_FILL_ARGS) { struct pwd *pwd; struct vnode *vp; char *fullpath = "unknown"; char *freepath = NULL; pwd = pwd_hold_proc(p); vp = jailed(p->p_ucred) ? pwd->pwd_jdir : pwd->pwd_rdir; vn_fullpath(vp, &fullpath, &freepath); sbuf_printf(sb, "%s", fullpath); if (freepath) free(freepath, M_TEMP); pwd_drop(pwd); return (0); } /* * Filler function for proc/pid/cmdline */ static int linprocfs_doproccmdline(PFS_FILL_ARGS) { int ret; PROC_LOCK(p); if ((ret = p_cansee(td, p)) != 0) { PROC_UNLOCK(p); return (ret); } /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwize. */ if (p->p_vmspace == &vmspace0) { PROC_UNLOCK(p); return (0); } if (p->p_args != NULL) { sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length); PROC_UNLOCK(p); return (0); } if ((p->p_flag & P_SYSTEM) != 0) { PROC_UNLOCK(p); return (0); } PROC_UNLOCK(p); ret = proc_getargv(td, p, sb); return (ret); } /* * Filler function for proc/pid/environ */ static int linprocfs_doprocenviron(PFS_FILL_ARGS) { /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwize. */ if (p->p_vmspace == &vmspace0) return (0); return (proc_getenvv(td, p, sb)); } static char l32_map_str[] = "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n"; static char l64_map_str[] = "%016lx-%016lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n"; static char vdso_str[] = " [vdso]"; static char stack_str[] = " [stack]"; /* * Filler function for proc/pid/maps */ static int linprocfs_doprocmaps(PFS_FILL_ARGS) { struct vmspace *vm; vm_map_t map; vm_map_entry_t entry, tmp_entry; vm_object_t obj, tobj, lobj; vm_offset_t e_start, e_end; vm_ooffset_t off; vm_prot_t e_prot; unsigned int last_timestamp; char *name = "", *freename = NULL; const char *l_map_str; ino_t ino; int error; struct vnode *vp; struct vattr vat; bool private; PROC_LOCK(p); error = p_candebug(td, p); PROC_UNLOCK(p); if (error) return (error); if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); error = 0; vm = vmspace_acquire_ref(p); if (vm == NULL) return (ESRCH); if (SV_CURPROC_FLAG(SV_LP64)) l_map_str = l64_map_str; else l_map_str = l32_map_str; map = &vm->vm_map; vm_map_lock_read(map); VM_MAP_ENTRY_FOREACH(entry, map) { name = ""; freename = NULL; /* * Skip printing of the guard page of the stack region, as * it confuses glibc pthread_getattr_np() method, where both * the base address and size of the stack of the initial thread * are calculated. */ if ((entry->eflags & (MAP_ENTRY_IS_SUB_MAP | MAP_ENTRY_GUARD)) != 0) continue; e_prot = entry->protection; e_start = entry->start; e_end = entry->end; obj = entry->object.vm_object; off = entry->offset; for (lobj = tobj = obj; tobj != NULL; lobj = tobj, tobj = tobj->backing_object) { VM_OBJECT_RLOCK(tobj); off += lobj->backing_object_offset; if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); } private = (entry->eflags & MAP_ENTRY_COW) != 0 || obj == NULL || (obj->flags & OBJ_ANON) != 0; last_timestamp = map->timestamp; vm_map_unlock_read(map); ino = 0; if (lobj) { vp = vm_object_vnode(lobj); if (vp != NULL) vref(vp); if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); VM_OBJECT_RUNLOCK(obj); if (vp != NULL) { vn_fullpath(vp, &name, &freename); vn_lock(vp, LK_SHARED | LK_RETRY); VOP_GETATTR(vp, &vat, td->td_ucred); ino = vat.va_fileid; vput(vp); } else if (SV_PROC_ABI(p) == SV_ABI_LINUX) { /* * sv_shared_page_base pointed out to the * FreeBSD sharedpage, PAGE_SIZE is a size * of it. The vDSO page is above. */ if (e_start == p->p_sysent->sv_shared_page_base + PAGE_SIZE) name = vdso_str; if (e_end == p->p_sysent->sv_usrstack) name = stack_str; } } /* * format: * start, end, access, offset, major, minor, inode, name. */ error = sbuf_printf(sb, l_map_str, (u_long)e_start, (u_long)e_end, (e_prot & VM_PROT_READ)?"r":"-", (e_prot & VM_PROT_WRITE)?"w":"-", (e_prot & VM_PROT_EXECUTE)?"x":"-", private ? "p" : "s", (u_long)off, 0, 0, (u_long)ino, *name ? " " : " ", name ); if (freename) free(freename, M_TEMP); vm_map_lock_read(map); if (error == -1) { error = 0; break; } if (last_timestamp != map->timestamp) { /* * Look again for the entry because the map was * modified while it was unlocked. Specifically, * the entry may have been clipped, merged, or deleted. */ vm_map_lookup_entry(map, e_end - 1, &tmp_entry); entry = tmp_entry; } } vm_map_unlock_read(map); vmspace_free(vm); return (error); } /* * Filler function for proc/pid/mem */ static int linprocfs_doprocmem(PFS_FILL_ARGS) { ssize_t resid; int error; resid = uio->uio_resid; error = procfs_doprocmem(PFS_FILL_ARGNAMES); if (uio->uio_rw == UIO_READ && resid != uio->uio_resid) return (0); if (error == EFAULT) error = EIO; return (error); } /* * Filler function for proc/net/dev */ static int linprocfs_donetdev_cb(if_t ifp, void *arg) { char ifname[LINUX_IFNAMSIZ]; struct sbuf *sb = arg; if (ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname)) <= 0) return (ENODEV); sbuf_printf(sb, "%6.6s: ", ifname); sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ", (uintmax_t)if_getcounter(ifp, IFCOUNTER_IBYTES), (uintmax_t)if_getcounter(ifp, IFCOUNTER_IPACKETS), (uintmax_t)if_getcounter(ifp, IFCOUNTER_IERRORS), (uintmax_t)if_getcounter(ifp, IFCOUNTER_IQDROPS), /* rx_missed_errors */ 0UL, /* rx_fifo_errors */ 0UL, /* rx_length_errors + * rx_over_errors + * rx_crc_errors + * rx_frame_errors */ 0UL, /* rx_compressed */ (uintmax_t)if_getcounter(ifp, IFCOUNTER_IMCASTS)); /* XXX-BZ rx only? */ sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n", (uintmax_t)if_getcounter(ifp, IFCOUNTER_OBYTES), (uintmax_t)if_getcounter(ifp, IFCOUNTER_OPACKETS), (uintmax_t)if_getcounter(ifp, IFCOUNTER_OERRORS), (uintmax_t)if_getcounter(ifp, IFCOUNTER_OQDROPS), 0UL, /* tx_fifo_errors */ (uintmax_t)if_getcounter(ifp, IFCOUNTER_COLLISIONS), 0UL, /* tx_carrier_errors + * tx_aborted_errors + * tx_window_errors + * tx_heartbeat_errors*/ 0UL); /* tx_compressed */ return (0); } static int linprocfs_donetdev(PFS_FILL_ARGS) { struct epoch_tracker et; sbuf_printf(sb, "%6s|%58s|%s\n" "%6s|%58s|%58s\n", "Inter-", " Receive", " Transmit", " face", "bytes packets errs drop fifo frame compressed multicast", "bytes packets errs drop fifo colls carrier compressed"); CURVNET_SET(TD_TO_VNET(curthread)); NET_EPOCH_ENTER(et); if_foreach(linprocfs_donetdev_cb, sb); NET_EPOCH_EXIT(et); CURVNET_RESTORE(); return (0); } struct walkarg { struct sbuf *sb; }; static int linux_route_print(struct rtentry *rt, void *vw) { #ifdef INET struct walkarg *w = vw; struct route_nhop_data rnd; struct in_addr dst, mask; struct nhop_object *nh; char ifname[16]; uint32_t scopeid = 0; uint32_t gw = 0; uint32_t linux_flags = 0; rt_get_inet_prefix_pmask(rt, &dst, &mask, &scopeid); rt_get_rnd(rt, &rnd); /* select only first route in case of multipath */ nh = nhop_select_func(rnd.rnd_nhop, 0); if (ifname_bsd_to_linux_ifp(nh->nh_ifp, ifname, sizeof(ifname)) <= 0) return (ENODEV); gw = (nh->nh_flags & NHF_GATEWAY) ? nh->gw4_sa.sin_addr.s_addr : 0; linux_flags = RTF_UP | (nhop_get_rtflags(nh) & (RTF_GATEWAY | RTF_HOST)); sbuf_printf(w->sb, "%s\t" "%08X\t%08X\t%04X\t" "%d\t%u\t%d\t" "%08X\t%d\t%u\t%u", ifname, dst.s_addr, gw, linux_flags, 0, 0, rnd.rnd_weight, mask.s_addr, nh->nh_mtu, 0, 0); sbuf_printf(w->sb, "\n\n"); #endif return (0); } /* * Filler function for proc/net/route */ static int linprocfs_donetroute(PFS_FILL_ARGS) { struct epoch_tracker et; struct walkarg w = { .sb = sb }; uint32_t fibnum = curthread->td_proc->p_fibnum; sbuf_printf(w.sb, "%-127s\n", "Iface\tDestination\tGateway " "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" "\tWindow\tIRTT"); CURVNET_SET(TD_TO_VNET(curthread)); NET_EPOCH_ENTER(et); rib_walk(fibnum, AF_INET, false, linux_route_print, &w); NET_EPOCH_EXIT(et); CURVNET_RESTORE(); return (0); } /* * Filler function for proc/sys/kernel/osrelease */ static int linprocfs_doosrelease(PFS_FILL_ARGS) { char osrelease[LINUX_MAX_UTSNAME]; linux_get_osrelease(td, osrelease); sbuf_printf(sb, "%s\n", osrelease); return (0); } /* * Filler function for proc/sys/kernel/ostype */ static int linprocfs_doostype(PFS_FILL_ARGS) { char osname[LINUX_MAX_UTSNAME]; linux_get_osname(td, osname); sbuf_printf(sb, "%s\n", osname); return (0); } /* * Filler function for proc/sys/kernel/version */ static int linprocfs_doosbuild(PFS_FILL_ARGS) { linprocfs_osbuild(td, sb); sbuf_cat(sb, "\n"); return (0); } /* * Filler function for proc/sys/kernel/msgmax */ static int linprocfs_domsgmax(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", msginfo.msgmax); return (0); } /* * Filler function for proc/sys/kernel/msgmni */ static int linprocfs_domsgmni(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", msginfo.msgmni); return (0); } /* * Filler function for proc/sys/kernel/msgmnb */ static int linprocfs_domsgmnb(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", msginfo.msgmnb); return (0); } /* * Filler function for proc/sys/kernel/ngroups_max * * Note that in Linux it defaults to 65536, not 1023. */ static int linprocfs_dongroups_max(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", ngroups_max); return (0); } /* * Filler function for proc/sys/kernel/pid_max */ static int linprocfs_dopid_max(PFS_FILL_ARGS) { sbuf_printf(sb, "%i\n", PID_MAX); return (0); } /* * Filler function for proc/sys/kernel/sem */ static int linprocfs_dosem(PFS_FILL_ARGS) { sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns, seminfo.semopm, seminfo.semmni); return (0); } /* * Filler function for proc/sys/kernel/shmall */ static int linprocfs_doshmall(PFS_FILL_ARGS) { sbuf_printf(sb, "%lu\n", shminfo.shmall); return (0); } /* * Filler function for proc/sys/kernel/shmmax */ static int linprocfs_doshmmax(PFS_FILL_ARGS) { sbuf_printf(sb, "%lu\n", shminfo.shmmax); return (0); } /* * Filler function for proc/sys/kernel/shmmni */ static int linprocfs_doshmmni(PFS_FILL_ARGS) { sbuf_printf(sb, "%lu\n", shminfo.shmmni); return (0); } /* * Filler function for proc/sys/kernel/tainted */ static int linprocfs_dotainted(PFS_FILL_ARGS) { sbuf_printf(sb, "0\n"); return (0); } /* * Filler function for proc/sys/vm/min_free_kbytes * * This mirrors the approach in illumos to return zero for reads. Effectively, * it says, no memory is kept in reserve for "atomic allocations". This class * of allocation can be used at times when a thread cannot be suspended. */ static int linprocfs_dominfree(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", 0); return (0); } /* * Filler function for proc/scsi/device_info */ static int linprocfs_doscsidevinfo(PFS_FILL_ARGS) { return (0); } /* * Filler function for proc/scsi/scsi */ static int linprocfs_doscsiscsi(PFS_FILL_ARGS) { return (0); } /* * Filler function for proc/devices */ static int linprocfs_dodevices(PFS_FILL_ARGS) { char *char_devices; sbuf_printf(sb, "Character devices:\n"); char_devices = linux_get_char_devices(); sbuf_printf(sb, "%s", char_devices); linux_free_get_char_devices(char_devices); sbuf_printf(sb, "\nBlock devices:\n"); return (0); } /* * Filler function for proc/cmdline */ static int linprocfs_docmdline(PFS_FILL_ARGS) { sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname); sbuf_printf(sb, " ro root=302\n"); return (0); } /* * Filler function for proc/filesystems */ static int linprocfs_dofilesystems(PFS_FILL_ARGS) { struct vfsconf *vfsp; vfsconf_slock(); TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { if (vfsp->vfc_flags & VFCF_SYNTHETIC) sbuf_printf(sb, "nodev"); sbuf_printf(sb, "\t%s\n", vfsp->vfc_name); } vfsconf_sunlock(); return(0); } /* * Filler function for proc/modules */ static int linprocfs_domodules(PFS_FILL_ARGS) { #if 0 struct linker_file *lf; TAILQ_FOREACH(lf, &linker_files, link) { sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename, (unsigned long)lf->size, lf->refs); } #endif return (0); } /* * Filler function for proc/pid/fd */ static int linprocfs_dofdescfs(PFS_FILL_ARGS) { if (p == curproc) sbuf_printf(sb, "/dev/fd"); else sbuf_printf(sb, "unknown"); return (0); } /* * Filler function for proc/pid/limits */ static const struct linux_rlimit_ident { const char *desc; const char *unit; unsigned int rlim_id; } linux_rlimits_ident[] = { { "Max cpu time", "seconds", RLIMIT_CPU }, { "Max file size", "bytes", RLIMIT_FSIZE }, { "Max data size", "bytes", RLIMIT_DATA }, { "Max stack size", "bytes", RLIMIT_STACK }, { "Max core file size", "bytes", RLIMIT_CORE }, { "Max resident set", "bytes", RLIMIT_RSS }, { "Max processes", "processes", RLIMIT_NPROC }, { "Max open files", "files", RLIMIT_NOFILE }, { "Max locked memory", "bytes", RLIMIT_MEMLOCK }, { "Max address space", "bytes", RLIMIT_AS }, { "Max file locks", "locks", LINUX_RLIMIT_LOCKS }, { "Max pending signals", "signals", LINUX_RLIMIT_SIGPENDING }, { "Max msgqueue size", "bytes", LINUX_RLIMIT_MSGQUEUE }, { "Max nice priority", "", LINUX_RLIMIT_NICE }, { "Max realtime priority", "", LINUX_RLIMIT_RTPRIO }, { "Max realtime timeout", "us", LINUX_RLIMIT_RTTIME }, { 0, 0, 0 } }; static int linprocfs_doproclimits(PFS_FILL_ARGS) { const struct linux_rlimit_ident *li; struct plimit *limp; struct rlimit rl; ssize_t size; int res, error; error = 0; PROC_LOCK(p); limp = lim_hold(p->p_limit); PROC_UNLOCK(p); size = sizeof(res); sbuf_printf(sb, "%-26s%-21s%-21s%-21s\n", "Limit", "Soft Limit", "Hard Limit", "Units"); for (li = linux_rlimits_ident; li->desc != NULL; ++li) { switch (li->rlim_id) { case LINUX_RLIMIT_LOCKS: /* FALLTHROUGH */ case LINUX_RLIMIT_RTTIME: rl.rlim_cur = RLIM_INFINITY; break; case LINUX_RLIMIT_SIGPENDING: error = kernel_sysctlbyname(td, "kern.sigqueue.max_pending_per_proc", &res, &size, 0, 0, 0, 0); if (error != 0) goto out; rl.rlim_cur = res; rl.rlim_max = res; break; case LINUX_RLIMIT_MSGQUEUE: error = kernel_sysctlbyname(td, "kern.ipc.msgmnb", &res, &size, 0, 0, 0, 0); if (error != 0) goto out; rl.rlim_cur = res; rl.rlim_max = res; break; case LINUX_RLIMIT_NICE: /* FALLTHROUGH */ case LINUX_RLIMIT_RTPRIO: rl.rlim_cur = 0; rl.rlim_max = 0; break; default: rl = limp->pl_rlimit[li->rlim_id]; break; } if (rl.rlim_cur == RLIM_INFINITY) sbuf_printf(sb, "%-26s%-21s%-21s%-10s\n", li->desc, "unlimited", "unlimited", li->unit); else sbuf_printf(sb, "%-26s%-21llu%-21llu%-10s\n", li->desc, (unsigned long long)rl.rlim_cur, (unsigned long long)rl.rlim_max, li->unit); } out: lim_free(limp); return (error); } /* * The point of the following two functions is to work around * an assertion in Chromium; see kern/240991 for details. */ static int linprocfs_dotaskattr(PFS_ATTR_ARGS) { vap->va_nlink = 3; return (0); } /* * Filler function for proc//task/.dummy */ static int linprocfs_dotaskdummy(PFS_FILL_ARGS) { return (0); } /* * Filler function for proc/sys/kernel/random/uuid */ static int linprocfs_douuid(PFS_FILL_ARGS) { struct uuid uuid; kern_uuidgen(&uuid, 1); sbuf_printf_uuid(sb, &uuid); sbuf_printf(sb, "\n"); return(0); } /* * Filler function for proc/sys/kernel/random/boot_id */ static int linprocfs_doboot_id(PFS_FILL_ARGS) { static bool firstboot = 1; static struct uuid uuid; if (firstboot) { kern_uuidgen(&uuid, 1); firstboot = 0; } sbuf_printf_uuid(sb, &uuid); sbuf_printf(sb, "\n"); return(0); } /* * Filler function for proc/pid/auxv */ static int linprocfs_doauxv(PFS_FILL_ARGS) { struct sbuf *asb; off_t buflen, resid; int error; /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwise. */ if (p->p_vmspace == &vmspace0) return (0); if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0 || uio->uio_resid < 0) return (EINVAL); asb = sbuf_new_auto(); if (asb == NULL) return (ENOMEM); error = proc_getauxv(td, p, asb); if (error == 0) error = sbuf_finish(asb); resid = sbuf_len(asb) - uio->uio_offset; if (resid > uio->uio_resid) buflen = uio->uio_resid; else buflen = resid; if (buflen > IOSIZE_MAX) return (EINVAL); if (buflen > maxphys) buflen = maxphys; if (resid <= 0) return (0); if (error == 0) error = uiomove(sbuf_data(asb) + uio->uio_offset, buflen, uio); sbuf_delete(asb); return (error); } /* * Filler function for proc/self/oom_score_adj */ static int linprocfs_do_oom_score_adj(PFS_FILL_ARGS) { struct linux_pemuldata *pem; long oom; pem = pem_find(p); if (pem == NULL || uio == NULL) return (EOPNOTSUPP); if (uio->uio_rw == UIO_READ) { sbuf_printf(sb, "%d\n", pem->oom_score_adj); } else { sbuf_trim(sb); sbuf_finish(sb); oom = strtol(sbuf_data(sb), NULL, 10); if (oom < LINUX_OOM_SCORE_ADJ_MIN || oom > LINUX_OOM_SCORE_ADJ_MAX) return (EINVAL); pem->oom_score_adj = oom; } return (0); } /* * Filler function for proc/sys/vm/max_map_count * * Maximum number of active map areas, on Linux this limits the number * of vmaps per mm struct. We don't limit mappings, return a suitable * large value. */ static int linprocfs_domax_map_cnt(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", INT32_MAX); return (0); } /* * Constructor */ static int linprocfs_init(PFS_INIT_ARGS) { struct pfs_node *root; struct pfs_node *dir; struct pfs_node *sys; root = pi->pi_root; /* /proc/... */ pfs_create_file(root, "cmdline", &linprocfs_docmdline, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "devices", &linprocfs_dodevices, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "filesystems", &linprocfs_dofilesystems, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "loadavg", &linprocfs_doloadavg, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "meminfo", &linprocfs_domeminfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "modules", &linprocfs_domodules, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "mounts", &linprocfs_domtab, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "mtab", &linprocfs_domtab, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "partitions", &linprocfs_dopartitions, NULL, NULL, NULL, PFS_RD); pfs_create_link(root, "self", &procfs_docurproc, NULL, NULL, NULL, 0); pfs_create_file(root, "stat", &linprocfs_dostat, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "swaps", &linprocfs_doswaps, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "uptime", &linprocfs_douptime, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "version", &linprocfs_doversion, NULL, NULL, NULL, PFS_RD); /* /proc/bus/... */ dir = pfs_create_dir(root, "bus", NULL, NULL, NULL, 0); dir = pfs_create_dir(dir, "pci", NULL, NULL, NULL, 0); dir = pfs_create_dir(dir, "devices", NULL, NULL, NULL, 0); /* /proc/net/... */ dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0); pfs_create_file(dir, "dev", &linprocfs_donetdev, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "route", &linprocfs_donetroute, NULL, NULL, NULL, PFS_RD); /* /proc//... */ dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP); pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline, NULL, NULL, NULL, PFS_RD); pfs_create_link(dir, "cwd", &linprocfs_doproccwd, NULL, NULL, NULL, 0); pfs_create_file(dir, "environ", &linprocfs_doprocenviron, NULL, &procfs_candebug, NULL, PFS_RD); pfs_create_link(dir, "exe", &procfs_doprocfile, NULL, &procfs_notsystem, NULL, 0); pfs_create_file(dir, "maps", &linprocfs_doprocmaps, NULL, NULL, NULL, PFS_RD | PFS_AUTODRAIN); pfs_create_file(dir, "mem", &linprocfs_doprocmem, procfs_attr_rw, &procfs_candebug, NULL, PFS_RDWR | PFS_RAW); pfs_create_file(dir, "mountinfo", &linprocfs_doprocmountinfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "mounts", &linprocfs_domtab, NULL, NULL, NULL, PFS_RD); pfs_create_link(dir, "root", &linprocfs_doprocroot, NULL, NULL, NULL, 0); pfs_create_file(dir, "stat", &linprocfs_doprocstat, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "statm", &linprocfs_doprocstatm, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "status", &linprocfs_doprocstatus, NULL, NULL, NULL, PFS_RD); pfs_create_link(dir, "fd", &linprocfs_dofdescfs, NULL, NULL, NULL, 0); pfs_create_file(dir, "auxv", &linprocfs_doauxv, NULL, &procfs_candebug, NULL, PFS_RD|PFS_RAWRD); pfs_create_file(dir, "limits", &linprocfs_doproclimits, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "oom_score_adj", &linprocfs_do_oom_score_adj, procfs_attr_rw, &procfs_candebug, NULL, PFS_RDWR); /* /proc//task/... */ dir = pfs_create_dir(dir, "task", linprocfs_dotaskattr, NULL, NULL, 0); pfs_create_file(dir, ".dummy", &linprocfs_dotaskdummy, NULL, NULL, NULL, PFS_RD); /* /proc/scsi/... */ dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0); pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi, NULL, NULL, NULL, PFS_RD); /* /proc/sys/... */ sys = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0); /* /proc/sys/kernel/... */ dir = pfs_create_dir(sys, "kernel", NULL, NULL, NULL, 0); pfs_create_file(dir, "osrelease", &linprocfs_doosrelease, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "ostype", &linprocfs_doostype, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "version", &linprocfs_doosbuild, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "msgmax", &linprocfs_domsgmax, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "msgmni", &linprocfs_domsgmni, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "msgmnb", &linprocfs_domsgmnb, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "ngroups_max", &linprocfs_dongroups_max, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "pid_max", &linprocfs_dopid_max, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "sem", &linprocfs_dosem, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "shmall", &linprocfs_doshmall, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "shmmax", &linprocfs_doshmmax, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "shmmni", &linprocfs_doshmmni, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "tainted", &linprocfs_dotainted, NULL, NULL, NULL, PFS_RD); /* /proc/sys/kernel/random/... */ dir = pfs_create_dir(dir, "random", NULL, NULL, NULL, 0); pfs_create_file(dir, "uuid", &linprocfs_douuid, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "boot_id", &linprocfs_doboot_id, NULL, NULL, NULL, PFS_RD); /* /proc/sys/vm/.... */ dir = pfs_create_dir(sys, "vm", NULL, NULL, NULL, 0); pfs_create_file(dir, "min_free_kbytes", &linprocfs_dominfree, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "max_map_count", &linprocfs_domax_map_cnt, NULL, NULL, NULL, PFS_RD); return (0); } /* * Destructor */ static int linprocfs_uninit(PFS_INIT_ARGS) { /* nothing to do, pseudofs will GC */ return (0); } PSEUDOFS(linprocfs, 1, VFCF_JAIL); #if defined(__aarch64__) || defined(__amd64__) MODULE_DEPEND(linprocfs, linux_common, 1, 1, 1); #else MODULE_DEPEND(linprocfs, linux, 1, 1, 1); #endif MODULE_DEPEND(linprocfs, procfs, 1, 1, 1); MODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1); MODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1); MODULE_DEPEND(linprocfs, sysvshm, 1, 1, 1); diff --git a/sys/dev/fb/splash_pcx.c b/sys/dev/fb/splash_pcx.c index e7fade552d04..d2b0b7719426 100644 --- a/sys/dev/fb/splash_pcx.c +++ b/sys/dev/fb/splash_pcx.c @@ -1,269 +1,269 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1999 Michael Smith * Copyright (c) 1999 Kazutaka YOKOTA - * Copyright (c) 1999 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include static int splash_mode = -1; static int splash_on = FALSE; static int pcx_start(video_adapter_t *adp); static int pcx_end(video_adapter_t *adp); static int pcx_splash(video_adapter_t *adp, int on); static int pcx_init(void *data, int sdepth); static int pcx_draw(video_adapter_t *adp); static splash_decoder_t pcx_decoder = { .name = "splash_pcx", .init = pcx_start, .term = pcx_end, .splash = pcx_splash, .data_type = SPLASH_IMAGE, }; SPLASH_DECODER(splash_pcx, pcx_decoder); static struct { int width; int height; int bpsl; int bpp; int planes; int zlen; const uint8_t *zdata; uint8_t *palette; } pcx_info; static int pcx_start(video_adapter_t *adp) { static int modes[] = { M_VGA_CG320, M_VESA_CG640x480, M_VESA_CG800x600, M_VESA_CG1024x768, -1, }; video_info_t info; int i; if (pcx_decoder.data == NULL || pcx_decoder.data_size <= 0 || pcx_init(pcx_decoder.data, pcx_decoder.data_size)) return (ENODEV); if (bootverbose) printf("splash_pcx: image good:\n" " width = %d\n" " height = %d\n" " depth = %d\n" " planes = %d\n", pcx_info.width, pcx_info.height, pcx_info.bpp, pcx_info.planes); for (i = 0; modes[i] >= 0; ++i) { if (vidd_get_info(adp, modes[i], &info) != 0) continue; if (bootverbose) printf("splash_pcx: considering mode %d:\n" " vi_width = %d\n" " vi_height = %d\n" " vi_depth = %d\n" " vi_planes = %d\n", modes[i], info.vi_width, info.vi_height, info.vi_depth, info.vi_planes); if (info.vi_width >= pcx_info.width && info.vi_height >= pcx_info.height && info.vi_depth == pcx_info.bpp && info.vi_planes == pcx_info.planes) break; } splash_mode = modes[i]; if (splash_mode == -1) return (ENODEV); if (bootverbose) printf("splash_pcx: selecting mode %d\n", splash_mode); return (0); } static int pcx_end(video_adapter_t *adp) { /* nothing to do */ return (0); } static int pcx_splash(video_adapter_t *adp, int on) { if (on) { if (!splash_on) { if (vidd_set_mode(adp, splash_mode) || pcx_draw(adp)) return 1; splash_on = TRUE; } return (0); } else { splash_on = FALSE; return (0); } } struct pcx_header { uint8_t manufactor; uint8_t version; uint8_t encoding; uint8_t bpp; uint16_t xmin; uint16_t ymin; uint16_t xmax; uint16_t ymax; uint16_t hres; uint16_t vres; uint8_t colormap[48]; uint8_t rsvd; uint8_t nplanes; uint16_t bpsl; uint16_t palinfo; uint16_t hsize; uint16_t vsize; }; #define MAXSCANLINE 1024 static int pcx_init(void *data, int size) { const struct pcx_header *hdr = data; if (size < 128 + 1 + 1 + 768 || hdr->manufactor != 10 || hdr->version != 5 || hdr->encoding != 1 || hdr->nplanes != 1 || hdr->bpp != 8 || hdr->bpsl > MAXSCANLINE || ((uint8_t *)data)[size - 769] != 12) { printf("splash_pcx: invalid PCX image\n"); return (1); } pcx_info.width = hdr->xmax - hdr->xmin + 1; pcx_info.height = hdr->ymax - hdr->ymin + 1; pcx_info.bpsl = hdr->bpsl; pcx_info.bpp = hdr->bpp; pcx_info.planes = hdr->nplanes; pcx_info.zlen = size - (128 + 1 + 768); pcx_info.zdata = (uint8_t *)data + 128; pcx_info.palette = (uint8_t *)data + size - 768; return (0); } static int pcx_draw(video_adapter_t *adp) { uint8_t *vidmem; int swidth, sheight, sbpsl; int banksize, origin; int c, i, j, pos, scan, x, y; uint8_t line[MAXSCANLINE]; if (pcx_info.zlen < 1) return (1); vidd_load_palette(adp, pcx_info.palette); vidmem = (uint8_t *)adp->va_window; swidth = adp->va_info.vi_width; sheight = adp->va_info.vi_height; sbpsl = adp->va_line_width; banksize = adp->va_window_size; for (origin = 0; origin < sheight*sbpsl; origin += banksize) { vidd_set_win_org(adp, origin); bzero(vidmem, banksize); } x = (swidth - pcx_info.width) / 2; y = (sheight - pcx_info.height) / 2; origin = 0; pos = y * sbpsl + x; while (pos > banksize) { pos -= banksize; origin += banksize; } vidd_set_win_org(adp, origin); for (scan = i = 0; scan < pcx_info.height; ++scan, ++y, pos += sbpsl) { for (j = 0; j < pcx_info.bpsl && i < pcx_info.zlen; ++i) { if ((pcx_info.zdata[i] & 0xc0) == 0xc0) { c = pcx_info.zdata[i++] & 0x3f; if (i >= pcx_info.zlen) return (1); } else { c = 1; } if (j + c > pcx_info.bpsl) return (1); while (c--) line[j++] = pcx_info.zdata[i]; } if (pos > banksize) { origin += banksize; pos -= banksize; vidd_set_win_org(adp, origin); } if (pos + pcx_info.width > banksize) { /* scanline crosses bank boundary */ j = banksize - pos; bcopy(line, vidmem + pos, j); origin += banksize; pos -= banksize; vidd_set_win_org(adp, origin); bcopy(line + j, vidmem, pcx_info.width - j); } else { bcopy(line, vidmem + pos, pcx_info.width); } } return (0); } diff --git a/sys/dev/syscons/logo/logo_saver.c b/sys/dev/syscons/logo/logo_saver.c index c209dc4032db..513d68be98e3 100644 --- a/sys/dev/syscons/logo/logo_saver.c +++ b/sys/dev/syscons/logo/logo_saver.c @@ -1,174 +1,174 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1998 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #define SAVER_NAME "logo_saver" #define SET_ORIGIN(adp, o) do { \ int oo = o; \ if (oo != last_origin) \ vidd_set_win_org(adp, last_origin = oo); \ } while (0) extern unsigned int logo_w; extern unsigned int logo_h; extern unsigned char logo_pal[]; extern unsigned char logo_img[]; extern unsigned int logo_img_size; static u_char *vid; static int banksize, scrmode, bpsl, scrw, scrh; static int blanked; static void logo_blit(video_adapter_t *adp, int x, int y) { int d, l, o, p; int last_origin = -1; for (o = 0, p = y * bpsl + x; p > banksize; p -= banksize) o += banksize; SET_ORIGIN(adp, o); for (d = 0; d < logo_img_size; d += logo_w) { if (p + logo_w < banksize) { bcopy(logo_img + d, vid + p, logo_w); p += bpsl; } else if (p < banksize) { l = banksize - p; bcopy(logo_img + d, vid + p, l); SET_ORIGIN(adp, (o += banksize)); bcopy(logo_img + d + l, vid, logo_w - l); p += bpsl - banksize; } else { p -= banksize; SET_ORIGIN(adp, (o += banksize)); bcopy(logo_img + d, vid + p, logo_w); p += bpsl; } } } static void logo_update(video_adapter_t *adp) { static int xpos = 0, ypos = 0; static int xinc = 1, yinc = 1; /* Turn when you hit the edge */ if ((xpos + logo_w + xinc > scrw) || (xpos + xinc < 0)) xinc = -xinc; if ((ypos + logo_h + yinc > scrh) || (ypos + yinc < 0)) yinc = -yinc; xpos += xinc; ypos += yinc; /* XXX Relies on margin around logo to erase trail */ logo_blit(adp, xpos, ypos); } static int logo_saver(video_adapter_t *adp, int blank) { int pl; if (blank) { /* switch to graphics mode */ if (blanked <= 0) { pl = splhigh(); vidd_set_mode(adp, scrmode); vidd_load_palette(adp, logo_pal); vidd_set_border(adp, 0); blanked++; vid = (u_char *)adp->va_window; banksize = adp->va_window_size; bpsl = adp->va_line_width; splx(pl); vidd_clear(adp); } logo_update(adp); } else { blanked = 0; } return (0); } static int logo_init(video_adapter_t *adp) { video_info_t info; if (!vidd_get_info(adp, M_VESA_CG800x600, &info)) { scrmode = M_VESA_CG800x600; } else if (!vidd_get_info(adp, M_VGA_CG320, &info)) { scrmode = M_VGA_CG320; } else { log(LOG_NOTICE, "%s: the console does not support M_VGA_CG320\n", SAVER_NAME); return (ENODEV); } scrw = info.vi_width; scrh = info.vi_height; return (0); } static int logo_term(video_adapter_t *adp) { return (0); } static scrn_saver_t logo_module = { SAVER_NAME, logo_init, logo_term, logo_saver, NULL }; #ifdef BEASTIE_LOGO SAVER_MODULE(beastie_saver, logo_module); #else SAVER_MODULE(logo_saver, logo_module); #endif diff --git a/sys/dev/syscons/rain/rain_saver.c b/sys/dev/syscons/rain/rain_saver.c index 06d26b6fe02d..7fce014bdbe4 100644 --- a/sys/dev/syscons/rain/rain_saver.c +++ b/sys/dev/syscons/rain/rain_saver.c @@ -1,177 +1,177 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1998 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #define SAVER_NAME "rain_saver" #ifdef MAX #undef MAX #endif #define MAX 63 /* number of colors (in addition to black) */ #define INCREMENT 4 /* increment between colors */ #define RED(n) ((n) * 3 + 0) #define GREEN(n) ((n) * 3 + 1) #define BLUE(n) ((n) * 3 + 2) #define SET_ORIGIN(adp, o) do { \ int oo = o; \ if (oo != last_origin) \ vidd_set_win_org(adp, last_origin = oo); \ } while (0) static u_char *vid; static int banksize, scrmode, bpsl, scrw, scrh; static u_char rain_pal[768]; static int blanked; static void rain_update(video_adapter_t *adp) { int i, t; t = rain_pal[BLUE(MAX)]; for (i = MAX; i > 1; i--) rain_pal[BLUE(i)] = rain_pal[BLUE(i - 1)]; rain_pal[BLUE(1)] = t; vidd_load_palette(adp, rain_pal); } static int rain_saver(video_adapter_t *adp, int blank) { int i, j, o, p, pl; u_char temp; int last_origin = -1; if (blank) { /* switch to graphics mode */ if (blanked <= 0) { pl = splhigh(); vidd_set_mode(adp, scrmode); vidd_load_palette(adp, rain_pal); vidd_set_border(adp, 0); blanked++; vid = (u_char *)adp->va_window; banksize = adp->va_window_size; bpsl = adp->va_line_width; splx(pl); for (i = 0; i < bpsl*scrh; i += banksize) { SET_ORIGIN(adp, i); if ((bpsl * scrh - i) < banksize) bzero(vid, bpsl * scrh - i); else bzero(vid, banksize); } SET_ORIGIN(adp, 0); for (i = 0, o = 0, p = 0; i < scrw; i += 2, p += 2) { if (p > banksize) { p -= banksize; o += banksize; SET_ORIGIN(adp, o); } vid[p] = 1 + (random() % MAX); } o = 0; p = 0; for (j = 1; j < scrh; j++) for (i = 0, p = bpsl * (j - 1) - o; i < scrw; i += 2, p+= 2) { while (p > banksize) { p -= banksize; o += banksize; } SET_ORIGIN(adp, o); temp = (vid[p] < MAX) ? 1 + vid[p] : 1; if (p + bpsl < banksize) { vid[p + bpsl] = temp; } else { SET_ORIGIN(adp, o + banksize); vid[p + bpsl - banksize] = temp; } } } /* update display */ rain_update(adp); } else { blanked = 0; } return (0); } static int rain_init(video_adapter_t *adp) { video_info_t info; int i; if (!vidd_get_info(adp, M_VGA_CG320, &info)) { scrmode = M_VGA_CG320; } else { log(LOG_NOTICE, "%s: the console does not support M_VGA_CG320\n", SAVER_NAME); return (ENODEV); } scrw = info.vi_width; scrh = info.vi_height; /* intialize the palette */ for (i = 1; i < MAX; i++) rain_pal[BLUE(i)] = rain_pal[BLUE(i - 1)] + INCREMENT; return (0); } static int rain_term(video_adapter_t *adp) { return (0); } static scrn_saver_t rain_module = { SAVER_NAME, rain_init, rain_term, rain_saver, NULL }; SAVER_MODULE(rain_saver, rain_module); diff --git a/sys/dev/syscons/warp/warp_saver.c b/sys/dev/syscons/warp/warp_saver.c index d1c79ea82d10..54f9fd4f1749 100644 --- a/sys/dev/syscons/warp/warp_saver.c +++ b/sys/dev/syscons/warp/warp_saver.c @@ -1,163 +1,163 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1998 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #define SAVER_NAME "warp_saver" #define SPP 15 #define STARS (SPP * (1 + 2 + 4 + 8)) #define SET_ORIGIN(adp, o) do { \ int oo = o; \ if (oo != last_origin) \ vidd_set_win_org(adp, last_origin = oo); \ } while (0) static u_char *vid; static int banksize, scrmode, bpsl, scrw, scrh; static int blanked; static int star[STARS]; static u_char warp_pal[768] = { 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x99, 0x99, 0x99, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff /* the rest is zero-filled by the compiler */ }; static void warp_update(video_adapter_t *adp) { int i, j, k, n, o, p; int last_origin = -1; for (i = 1, k = 0, n = SPP*8; i < 5; i++, n /= 2) { for (j = 0; j < n; j++, k++) { p = (star[k] / scrw) * bpsl + (star[k] % scrw); o = 0; while (p > banksize) { p -= banksize; o += banksize; } SET_ORIGIN(adp, o); vid[p] = 0; star[k] += i; if (star[k] > scrw*scrh) star[k] -= scrw*scrh; p = (star[k] / scrw) * bpsl + (star[k] % scrw); o = 0; while (p > banksize) { p -= banksize; o += banksize; } SET_ORIGIN(adp, o); vid[p] = i; } } } static int warp_saver(video_adapter_t *adp, int blank) { int pl; if (blank) { /* switch to graphics mode */ if (blanked <= 0) { pl = splhigh(); vidd_set_mode(adp, scrmode); vidd_load_palette(adp, warp_pal); vidd_set_border(adp, 0); blanked++; vid = (u_char *)adp->va_window; banksize = adp->va_window_size; bpsl = adp->va_line_width; splx(pl); vidd_clear(adp); } /* update display */ warp_update(adp); } else { blanked = 0; } return (0); } static int warp_init(video_adapter_t *adp) { video_info_t info; int i; if (!vidd_get_info(adp, M_VGA_CG320, &info)) { scrmode = M_VGA_CG320; } else { log(LOG_NOTICE, "%s: the console does not support M_VGA_CG320\n", SAVER_NAME); return (ENODEV); } scrw = info.vi_width; scrh = info.vi_height; /* randomize the star field */ for (i = 0; i < STARS; i++) star[i] = random() % (scrw * scrh); return (0); } static int warp_term(video_adapter_t *adp) { return (0); } static scrn_saver_t warp_module = { SAVER_NAME, warp_init, warp_term, warp_saver, NULL }; SAVER_MODULE(warp_saver, warp_module); diff --git a/sys/dev/usb/serial/ucycom.c b/sys/dev/usb/serial/ucycom.c index d1e975516c90..664cb7f05263 100644 --- a/sys/dev/usb/serial/ucycom.c +++ b/sys/dev/usb/serial/ucycom.c @@ -1,610 +1,610 @@ #include /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2004 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to * RS232 bridges. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ #define UCYCOM_IFACE_INDEX 0 enum { UCYCOM_CTRL_RD, UCYCOM_INTR_RD, UCYCOM_N_TRANSFER, }; struct ucycom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_device *sc_udev; struct usb_xfer *sc_xfer[UCYCOM_N_TRANSFER]; struct mtx sc_mtx; uint32_t sc_model; #define MODEL_CY7C63743 0x63743 #define MODEL_CY7C64013 0x64013 uint16_t sc_flen; /* feature report length */ uint16_t sc_ilen; /* input report length */ uint16_t sc_olen; /* output report length */ uint8_t sc_fid; /* feature report id */ uint8_t sc_iid; /* input report id */ uint8_t sc_oid; /* output report id */ uint8_t sc_cfg; #define UCYCOM_CFG_RESET 0x80 #define UCYCOM_CFG_PARODD 0x20 #define UCYCOM_CFG_PAREN 0x10 #define UCYCOM_CFG_STOPB 0x08 #define UCYCOM_CFG_DATAB 0x03 uint8_t sc_ist; /* status flags from last input */ uint8_t sc_iface_no; uint8_t sc_temp_cfg[32]; }; /* prototypes */ static device_probe_t ucycom_probe; static device_attach_t ucycom_attach; static device_detach_t ucycom_detach; static void ucycom_free_softc(struct ucycom_softc *); static usb_callback_t ucycom_ctrl_write_callback; static usb_callback_t ucycom_intr_read_callback; static void ucycom_free(struct ucom_softc *); static void ucycom_cfg_open(struct ucom_softc *); static void ucycom_start_read(struct ucom_softc *); static void ucycom_stop_read(struct ucom_softc *); static void ucycom_start_write(struct ucom_softc *); static void ucycom_stop_write(struct ucom_softc *); static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); static int ucycom_pre_param(struct ucom_softc *, struct termios *); static void ucycom_cfg_param(struct ucom_softc *, struct termios *); static void ucycom_poll(struct ucom_softc *ucom); static const struct usb_config ucycom_config[UCYCOM_N_TRANSFER] = { [UCYCOM_CTRL_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = (sizeof(struct usb_device_request) + UCYCOM_MAX_IOLEN), .callback = &ucycom_ctrl_write_callback, .timeout = 1000, /* 1 second */ }, [UCYCOM_INTR_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = UCYCOM_MAX_IOLEN, .callback = &ucycom_intr_read_callback, }, }; static const struct ucom_callback ucycom_callback = { .ucom_cfg_param = &ucycom_cfg_param, .ucom_cfg_open = &ucycom_cfg_open, .ucom_pre_param = &ucycom_pre_param, .ucom_start_read = &ucycom_start_read, .ucom_stop_read = &ucycom_stop_read, .ucom_start_write = &ucycom_start_write, .ucom_stop_write = &ucycom_stop_write, .ucom_poll = &ucycom_poll, .ucom_free = &ucycom_free, }; static device_method_t ucycom_methods[] = { DEVMETHOD(device_probe, ucycom_probe), DEVMETHOD(device_attach, ucycom_attach), DEVMETHOD(device_detach, ucycom_detach), DEVMETHOD_END }; static driver_t ucycom_driver = { .name = "ucycom", .methods = ucycom_methods, .size = sizeof(struct ucycom_softc), }; /* * Supported devices */ static const STRUCT_USB_HOST_ID ucycom_devs[] = { {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, }; DRIVER_MODULE(ucycom, uhub, ucycom_driver, NULL, NULL); MODULE_DEPEND(ucycom, ucom, 1, 1, 1); MODULE_DEPEND(ucycom, usb, 1, 1, 1); MODULE_DEPEND(ucycom, hid, 1, 1, 1); MODULE_VERSION(ucycom, 1); USB_PNP_HOST_INFO(ucycom_devs); #define UCYCOM_DEFAULT_RATE 4800 #define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ static int ucycom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); } static int ucycom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ucycom_softc *sc = device_get_softc(dev); void *urd_ptr = NULL; int32_t error; uint16_t urd_len; uint8_t iface_index; sc->sc_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ucycom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); DPRINTF("\n"); /* get chip model */ sc->sc_model = USB_GET_DRIVER_INFO(uaa); if (sc->sc_model == 0) { device_printf(dev, "unsupported device\n"); goto detach; } device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); /* get report descriptor */ error = usbd_req_get_hid_desc(uaa->device, NULL, &urd_ptr, &urd_len, M_USBDEV, UCYCOM_IFACE_INDEX); if (error) { device_printf(dev, "failed to get report " "descriptor: %s\n", usbd_errstr(error)); goto detach; } /* get report sizes */ sc->sc_flen = hid_report_size_max(urd_ptr, urd_len, hid_feature, &sc->sc_fid); sc->sc_ilen = hid_report_size_max(urd_ptr, urd_len, hid_input, &sc->sc_iid); sc->sc_olen = hid_report_size_max(urd_ptr, urd_len, hid_output, &sc->sc_oid); if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", sc->sc_ilen, sc->sc_olen, sc->sc_flen, UCYCOM_MAX_IOLEN); goto detach; } sc->sc_iface_no = uaa->info.bIfaceNum; iface_index = UCYCOM_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed\n"); goto detach; } error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &ucycom_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); if (urd_ptr) { free(urd_ptr, M_USBDEV); } return (0); /* success */ detach: if (urd_ptr) { free(urd_ptr, M_USBDEV); } ucycom_detach(dev); return (ENXIO); } static int ucycom_detach(device_t dev) { struct ucycom_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER); device_claim_softc(dev); ucycom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(ucycom); static void ucycom_free_softc(struct ucycom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void ucycom_free(struct ucom_softc *ucom) { ucycom_free_softc(ucom->sc_parent); } static void ucycom_cfg_open(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; /* set default configuration */ ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); } static void ucycom_start_read(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); } static void ucycom_stop_read(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); } static void ucycom_start_write(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); } static void ucycom_stop_write(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); } static void ucycom_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ucycom_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc0, *pc1; uint8_t data[2]; uint8_t offset; uint32_t actlen; pc0 = usbd_xfer_get_frame(xfer, 0); pc1 = usbd_xfer_get_frame(xfer, 1); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: case USB_ST_SETUP: switch (sc->sc_model) { case MODEL_CY7C63743: offset = 1; break; case MODEL_CY7C64013: offset = 2; break; default: offset = 0; break; } if (ucom_get_data(&sc->sc_ucom, pc1, offset, sc->sc_olen - offset, &actlen)) { req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, sc->sc_olen); switch (sc->sc_model) { case MODEL_CY7C63743: data[0] = actlen; break; case MODEL_CY7C64013: data[0] = 0; data[1] = actlen; break; default: break; } usbd_copy_in(pc0, 0, &req, sizeof(req)); usbd_copy_in(pc1, 0, data, offset); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, sc->sc_olen); usbd_xfer_set_frames(xfer, sc->sc_olen ? 2 : 1); usbd_transfer_submit(xfer); } return; default: /* Error */ if (error == USB_ERR_CANCELLED) { return; } DPRINTF("error=%s\n", usbd_errstr(error)); goto tr_transferred; } } static void ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) { struct usb_device_request req; uint16_t len; usb_error_t err; len = sc->sc_flen; if (len > sizeof(sc->sc_temp_cfg)) { len = sizeof(sc->sc_temp_cfg); } sc->sc_cfg = cfg; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, len); sc->sc_temp_cfg[0] = (baud & 0xff); sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; sc->sc_temp_cfg[4] = cfg; err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, sc->sc_temp_cfg, 0, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } } static int ucycom_pre_param(struct ucom_softc *ucom, struct termios *t) { switch (t->c_ospeed) { case 600: case 1200: case 2400: case 4800: case 9600: case 19200: case 38400: case 57600: #if 0 /* * Stock chips only support standard baud rates in the 600 - 57600 * range, but higher rates can be achieved using custom firmware. */ case 115200: case 153600: case 192000: #endif break; default: return (EINVAL); } return (0); } static void ucycom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct ucycom_softc *sc = ucom->sc_parent; uint8_t cfg; DPRINTF("\n"); if (t->c_cflag & CIGNORE) { cfg = sc->sc_cfg; } else { cfg = 0; switch (t->c_cflag & CSIZE) { default: case CS8: ++cfg; case CS7: ++cfg; case CS6: ++cfg; case CS5: break; } if (t->c_cflag & CSTOPB) cfg |= UCYCOM_CFG_STOPB; if (t->c_cflag & PARENB) cfg |= UCYCOM_CFG_PAREN; if (t->c_cflag & PARODD) cfg |= UCYCOM_CFG_PARODD; } ucycom_cfg_write(sc, t->c_ospeed, cfg); } static void ucycom_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ucycom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[2]; uint32_t offset; int len; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); pc = usbd_xfer_get_frame(xfer, 0); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: switch (sc->sc_model) { case MODEL_CY7C63743: if (actlen < 1) { goto tr_setup; } usbd_copy_out(pc, 0, buf, 1); sc->sc_ist = buf[0] & ~0x07; len = buf[0] & 0x07; actlen--; offset = 1; break; case MODEL_CY7C64013: if (actlen < 2) { goto tr_setup; } usbd_copy_out(pc, 0, buf, 2); sc->sc_ist = buf[0] & ~0x07; len = buf[1]; actlen -= 2; offset = 2; break; default: DPRINTFN(0, "unsupported model number\n"); goto tr_setup; } if (len > actlen) len = actlen; if (len) ucom_put_data(&sc->sc_ucom, pc, offset, len); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, sc->sc_ilen); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ucycom_poll(struct ucom_softc *ucom) { struct ucycom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UCYCOM_N_TRANSFER); } diff --git a/sys/fs/pseudofs/pseudofs.c b/sys/fs/pseudofs/pseudofs.c index 47a812262fad..eb4ca8a82456 100644 --- a/sys/fs/pseudofs/pseudofs.c +++ b/sys/fs/pseudofs/pseudofs.c @@ -1,513 +1,513 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes"); SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "pseudofs"); #ifdef PSEUDOFS_TRACE int pfs_trace; SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0, "enable tracing of pseudofs vnode operations"); #endif #if PFS_FSNAMELEN != MFSNAMELEN #error "PFS_FSNAMELEN is not equal to MFSNAMELEN" #endif /* * Allocate and initialize a node */ static struct pfs_node * pfs_alloc_node_flags(struct pfs_info *pi, const char *name, pfs_type_t type, int flags) { struct pfs_node *pn; int malloc_flags; size_t len; len = strlen(name); KASSERT(len < PFS_NAMELEN, ("%s(): node name is too long", __func__)); if (flags & PFS_NOWAIT) malloc_flags = M_NOWAIT | M_ZERO; else malloc_flags = M_WAITOK | M_ZERO; pn = malloc(sizeof(*pn) + len + 1, M_PFSNODES, malloc_flags); if (pn == NULL) return (NULL); mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK); memcpy(pn->pn_name, name, len); pn->pn_type = type; pn->pn_info = pi; return (pn); } static struct pfs_node * pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type) { return (pfs_alloc_node_flags(pi, name, type, 0)); } /* * Add a node to a directory */ static void pfs_add_node(struct pfs_node *parent, struct pfs_node *pn) { #ifdef INVARIANTS struct pfs_node *iter; #endif KASSERT(parent != NULL, ("%s(): parent is NULL", __func__)); KASSERT(pn->pn_parent == NULL, ("%s(): node already has a parent", __func__)); KASSERT(parent->pn_info != NULL, ("%s(): parent has no pn_info", __func__)); KASSERT(parent->pn_type == pfstype_dir || parent->pn_type == pfstype_procdir || parent->pn_type == pfstype_root, ("%s(): parent is not a directory", __func__)); #ifdef INVARIANTS /* XXX no locking! */ if (pn->pn_type == pfstype_procdir) for (iter = parent; iter != NULL; iter = iter->pn_parent) KASSERT(iter->pn_type != pfstype_procdir, ("%s(): nested process directories", __func__)); for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) { KASSERT(strcmp(pn->pn_name, iter->pn_name) != 0, ("%s(): homonymous siblings", __func__)); if (pn->pn_type == pfstype_procdir) KASSERT(iter->pn_type != pfstype_procdir, ("%s(): sibling process directories", __func__)); } #endif pn->pn_parent = parent; pfs_fileno_alloc(pn); pfs_lock(parent); if ((parent->pn_flags & PFS_PROCDEP) != 0) pn->pn_flags |= PFS_PROCDEP; if (parent->pn_nodes == NULL) { KASSERT(parent->pn_last_node == NULL, ("%s(): pn_last_node not NULL", __func__)); parent->pn_nodes = pn; parent->pn_last_node = pn; } else { KASSERT(parent->pn_last_node != NULL, ("%s(): pn_last_node is NULL", __func__)); KASSERT(parent->pn_last_node->pn_next == NULL, ("%s(): pn_last_node->pn_next not NULL", __func__)); parent->pn_last_node->pn_next = pn; parent->pn_last_node = pn; } pfs_unlock(parent); } /* * Detach a node from its aprent */ static void pfs_detach_node(struct pfs_node *pn) { struct pfs_node *node, *parent = pn->pn_parent; struct pfs_node **iter; KASSERT(parent != NULL, ("%s(): node has no parent", __func__)); KASSERT(parent->pn_info == pn->pn_info, ("%s(): parent has different pn_info", __func__)); pfs_lock(parent); if (pn == parent->pn_last_node) { if (pn == pn->pn_nodes) { parent->pn_last_node = NULL; } else { for (node = parent->pn_nodes; node->pn_next != pn; node = node->pn_next) continue; parent->pn_last_node = node; } } iter = &parent->pn_nodes; while (*iter != NULL) { if (*iter == pn) { *iter = pn->pn_next; break; } iter = &(*iter)->pn_next; } pn->pn_parent = NULL; pfs_unlock(parent); } /* * Add . and .. to a directory */ static int pfs_fixup_dir_flags(struct pfs_node *parent, int flags) { struct pfs_node *dot, *dotdot; dot = pfs_alloc_node_flags(parent->pn_info, ".", pfstype_this, flags); if (dot == NULL) return (ENOMEM); dotdot = pfs_alloc_node_flags(parent->pn_info, "..", pfstype_parent, flags); if (dotdot == NULL) { pfs_destroy(dot); return (ENOMEM); } pfs_add_node(parent, dot); pfs_add_node(parent, dotdot); return (0); } static void pfs_fixup_dir(struct pfs_node *parent) { pfs_fixup_dir_flags(parent, 0); } /* * Create a directory */ struct pfs_node * pfs_create_dir(struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; int rc; pn = pfs_alloc_node_flags(parent->pn_info, name, (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir, flags); if (pn == NULL) return (NULL); pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); rc = pfs_fixup_dir_flags(pn, flags); if (rc) { pfs_destroy(pn); return (NULL); } return (pn); } /* * Create a file */ struct pfs_node * pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_file, flags); if (pn == NULL) return (NULL); pn->pn_fill = fill; pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); return (pn); } /* * Create a symlink */ struct pfs_node * pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_symlink, flags); if (pn == NULL) return (NULL); pn->pn_fill = fill; pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); return (pn); } /* * Locate a node by name */ struct pfs_node * pfs_find_node(struct pfs_node *parent, const char *name) { struct pfs_node *pn; pfs_lock(parent); for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next) if (strcmp(pn->pn_name, name) == 0) break; pfs_unlock(parent); return (pn); } /* * Destroy a node and all its descendants. If the node to be destroyed * has a parent, the parent's mutex must be held. */ int pfs_destroy(struct pfs_node *pn) { struct pfs_node *iter; KASSERT(pn != NULL, ("%s(): node is NULL", __func__)); KASSERT(pn->pn_info != NULL, ("%s(): node has no pn_info", __func__)); if (pn->pn_parent) pfs_detach_node(pn); /* destroy children */ if (pn->pn_type == pfstype_dir || pn->pn_type == pfstype_procdir || pn->pn_type == pfstype_root) { pfs_lock(pn); while (pn->pn_nodes != NULL) { iter = pn->pn_nodes; pn->pn_nodes = iter->pn_next; iter->pn_parent = NULL; pfs_unlock(pn); pfs_destroy(iter); pfs_lock(pn); } pfs_unlock(pn); } /* revoke vnodes and fileno */ pfs_purge(pn); /* callback to free any private resources */ if (pn->pn_destroy != NULL) pn_destroy(pn); /* destroy the node */ pfs_fileno_free(pn); mtx_destroy(&pn->pn_mutex); free(pn, M_PFSNODES); return (0); } /* * Mount a pseudofs instance */ int pfs_mount(struct pfs_info *pi, struct mount *mp) { struct statfs *sbp; if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; mp->mnt_kern_flag |= MNTK_NOMSYNC; MNT_IUNLOCK(mp); mp->mnt_data = pi; vfs_getnewfsid(mp); sbp = &mp->mnt_stat; vfs_mountedfrom(mp, pi->pi_name); sbp->f_bsize = PAGE_SIZE; sbp->f_iosize = PAGE_SIZE; sbp->f_blocks = 2; sbp->f_bfree = 2; sbp->f_bavail = 2; sbp->f_files = 0; sbp->f_ffree = 0; return (0); } /* * Compatibility shim for old mount(2) system call */ int pfs_cmount(struct mntarg *ma, void *data, uint64_t flags) { int error; error = kernel_mount(ma, flags); return (error); } /* * Unmount a pseudofs instance */ int pfs_unmount(struct mount *mp, int mntflags) { int error; error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0, curthread); return (error); } /* * Return a root vnode */ int pfs_root(struct mount *mp, int flags, struct vnode **vpp) { struct pfs_info *pi; pi = (struct pfs_info *)mp->mnt_data; return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID)); } /* * Return filesystem stats */ int pfs_statfs(struct mount *mp, struct statfs *sbp) { /* no-op: always called with mp->mnt_stat */ return (0); } /* * Initialize a pseudofs instance */ int pfs_init(struct pfs_info *pi, struct vfsconf *vfc) { struct pfs_node *root; int error; pfs_fileno_init(pi); /* set up the root directory */ root = pfs_alloc_node(pi, "/", pfstype_root); pi->pi_root = root; pfs_fileno_alloc(root); pfs_fixup_dir(root); /* construct file hierarchy */ error = (pi->pi_init)(pi, vfc); if (error) { pfs_destroy(root); pi->pi_root = NULL; return (error); } if (bootverbose) printf("%s registered\n", pi->pi_name); return (0); } /* * Destroy a pseudofs instance */ int pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) { int error; pfs_destroy(pi->pi_root); pi->pi_root = NULL; pfs_fileno_uninit(pi); if (bootverbose) printf("%s unregistered\n", pi->pi_name); error = (pi->pi_uninit)(pi, vfc); return (error); } /* * Handle load / unload events */ static int pfs_modevent(module_t mod, int evt, void *arg) { switch (evt) { case MOD_LOAD: pfs_vncache_load(); break; case MOD_UNLOAD: case MOD_SHUTDOWN: pfs_vncache_unload(); break; default: return EOPNOTSUPP; break; } return 0; } /* * Module declaration */ static moduledata_t pseudofs_data = { "pseudofs", pfs_modevent, NULL }; DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST); MODULE_VERSION(pseudofs, 1); diff --git a/sys/fs/pseudofs/pseudofs.h b/sys/fs/pseudofs/pseudofs.h index c28d7cbb8078..c60dd7b339d1 100644 --- a/sys/fs/pseudofs/pseudofs.h +++ b/sys/fs/pseudofs/pseudofs.h @@ -1,312 +1,312 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _PSEUDOFS_H_INCLUDED #define _PSEUDOFS_H_INCLUDED #include /* * Opaque structures */ struct mntarg; struct mount; struct nameidata; struct proc; struct sbuf; struct statfs; struct thread; struct uio; struct vfsconf; struct vnode; /* * Limits and constants */ #define PFS_NAMELEN 128 #define PFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ #define PFS_DELEN (offsetof(struct dirent, d_name) + PFS_NAMELEN) typedef enum { pfstype_none = 0, pfstype_root, pfstype_dir, pfstype_this, pfstype_parent, pfstype_file, pfstype_symlink, pfstype_procdir } pfs_type_t; /* * Flags */ #define PFS_RD 0x0001 /* readable */ #define PFS_WR 0x0002 /* writeable */ #define PFS_RDWR (PFS_RD|PFS_WR) #define PFS_RAWRD 0x0004 /* raw reader */ #define PFS_RAWWR 0x0008 /* raw writer */ #define PFS_RAW (PFS_RAWRD|PFS_RAWWR) #define PFS_PROCDEP 0x0010 /* process-dependent */ #define PFS_NOWAIT 0x0020 /* allow malloc to fail */ #define PFS_AUTODRAIN 0x0040 /* sbuf_print can sleep to drain */ /* * Data structures */ struct pfs_info; struct pfs_node; /* * Init / uninit callback */ #define PFS_INIT_ARGS \ struct pfs_info *pi, struct vfsconf *vfc #define PFS_INIT_ARGNAMES \ pi, vfc #define PFS_INIT_PROTO(name) \ int name(PFS_INIT_ARGS); typedef int (*pfs_init_t)(PFS_INIT_ARGS); /* * Filler callback * Called with proc held but unlocked */ #define PFS_FILL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct sbuf *sb, struct uio *uio #define PFS_FILL_ARGNAMES \ td, p, pn, sb, uio #define PFS_FILL_PROTO(name) \ int name(PFS_FILL_ARGS); typedef int (*pfs_fill_t)(PFS_FILL_ARGS); /* * Attribute callback * Called with proc locked */ struct vattr; #define PFS_ATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct vattr *vap #define PFS_ATTR_ARGNAMES \ td, p, pn, vap #define PFS_ATTR_PROTO(name) \ int name(PFS_ATTR_ARGS); typedef int (*pfs_attr_t)(PFS_ATTR_ARGS); /* * Visibility callback * Called with proc locked */ #define PFS_VIS_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_VIS_ARGNAMES \ td, p, pn #define PFS_VIS_PROTO(name) \ int name(PFS_VIS_ARGS); typedef int (*pfs_vis_t)(PFS_VIS_ARGS); /* * Ioctl callback * Called with proc locked */ #define PFS_IOCTL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ unsigned long cmd, void *data #define PFS_IOCTL_ARGNAMES \ td, p, pn, cmd, data #define PFS_IOCTL_PROTO(name) \ int name(PFS_IOCTL_ARGS); typedef int (*pfs_ioctl_t)(PFS_IOCTL_ARGS); /* * Getextattr callback * Called with proc locked */ #define PFS_GETEXTATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ int attrnamespace, const char *name, struct uio *uio, \ size_t *size, struct ucred *cred #define PFS_GETEXTATTR_ARGNAMES \ td, p, pn, attrnamespace, name, uio, size, cred #define PFS_GETEXTATTR_PROTO(name) \ int name(PFS_GETEXTATTR_ARGS); struct ucred; typedef int (*pfs_getextattr_t)(PFS_GETEXTATTR_ARGS); /* * Last-close callback * Called with proc locked */ #define PFS_CLOSE_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_CLOSE_ARGNAMES \ td, p, pn #define PFS_CLOSE_PROTO(name) \ int name(PFS_CLOSE_ARGS); typedef int (*pfs_close_t)(PFS_CLOSE_ARGS); /* * Destroy callback */ #define PFS_DESTROY_ARGS \ struct pfs_node *pn #define PFS_DESTROY_ARGNAMES \ pn #define PFS_DESTROY_PROTO(name) \ int name(PFS_DESTROY_ARGS); typedef int (*pfs_destroy_t)(PFS_DESTROY_ARGS); /* * pfs_info: describes a pseudofs instance * * The pi_mutex is only used to avoid using the global subr_unit lock * for unrhdr. The rest of struct pfs_info is only modified during * vfs_init() and vfs_uninit() of the consumer filesystem. */ struct pfs_info { char pi_name[PFS_FSNAMELEN]; pfs_init_t pi_init; pfs_init_t pi_uninit; /* members below this line are initialized at run time */ struct pfs_node *pi_root; struct mtx pi_mutex; struct unrhdr *pi_unrhdr; }; /* * pfs_node: describes a node (file or directory) within a pseudofs * * - Fields marked (o) are protected by the node's own mutex. * - Fields marked (p) are protected by the node's parent's mutex. * - Remaining fields are not protected by any lock and are assumed to be * immutable once the node has been created. * * To prevent deadlocks, if a node's mutex is to be held at the same time * as its parent's (e.g. when adding or removing nodes to a directory), * the parent's mutex must always be acquired first. Unfortunately, this * is not enforcable by WITNESS. */ struct pfs_node { pfs_type_t pn_type; int pn_flags; struct mtx pn_mutex; void *pn_data; /* (o) */ pfs_fill_t pn_fill; pfs_ioctl_t pn_ioctl; pfs_close_t pn_close; pfs_attr_t pn_attr; pfs_vis_t pn_vis; pfs_getextattr_t pn_getextattr; pfs_destroy_t pn_destroy; struct pfs_info *pn_info; u_int32_t pn_fileno; /* (o) */ struct pfs_node *pn_parent; /* (o) */ struct pfs_node *pn_nodes; /* (o) */ struct pfs_node *pn_last_node; /* (o) */ struct pfs_node *pn_next; /* (p) */ char pn_name[]; /* Keep it last */ }; /* * VFS interface */ int pfs_mount (struct pfs_info *pi, struct mount *mp); int pfs_cmount (struct mntarg *ma, void *data, uint64_t flags); int pfs_unmount (struct mount *mp, int mntflags); int pfs_root (struct mount *mp, int flags, struct vnode **vpp); int pfs_statfs (struct mount *mp, struct statfs *sbp); int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); /* * Directory structure construction and manipulation */ struct pfs_node *pfs_create_dir (struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_find_node (struct pfs_node *parent, const char *name); void pfs_purge (struct pfs_node *pn); int pfs_destroy (struct pfs_node *pn); /* * Now for some initialization magic... */ #define PSEUDOFS(name, version, flags) \ \ static struct pfs_info name##_info = { \ #name, \ name##_init, \ name##_uninit, \ }; \ \ static int \ _##name##_mount(struct mount *mp) { \ return (pfs_mount(&name##_info, mp)); \ } \ \ static int \ _##name##_init(struct vfsconf *vfc) { \ return (pfs_init(&name##_info, vfc)); \ } \ \ static int \ _##name##_uninit(struct vfsconf *vfc) { \ return (pfs_uninit(&name##_info, vfc)); \ } \ \ static struct vfsops name##_vfsops = { \ .vfs_cmount = pfs_cmount, \ .vfs_init = _##name##_init, \ .vfs_mount = _##name##_mount, \ .vfs_root = pfs_root, \ .vfs_statfs = pfs_statfs, \ .vfs_uninit = _##name##_uninit, \ .vfs_unmount = pfs_unmount, \ }; \ VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC | flags); \ MODULE_VERSION(name, version); \ MODULE_DEPEND(name, pseudofs, 1, 1, 1); #endif diff --git a/sys/fs/pseudofs/pseudofs_fileno.c b/sys/fs/pseudofs/pseudofs_fileno.c index 8d526994e299..3c1b18bdda1e 100644 --- a/sys/fs/pseudofs/pseudofs_fileno.c +++ b/sys/fs/pseudofs/pseudofs_fileno.c @@ -1,157 +1,157 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include /* * Initialize fileno bitmap */ void pfs_fileno_init(struct pfs_info *pi) { mtx_init(&pi->pi_mutex, "pfs_fileno", NULL, MTX_DEF); pi->pi_unrhdr = new_unrhdr(3, INT_MAX / NO_PID, &pi->pi_mutex); } /* * Tear down fileno bitmap */ void pfs_fileno_uninit(struct pfs_info *pi) { delete_unrhdr(pi->pi_unrhdr); pi->pi_unrhdr = NULL; mtx_destroy(&pi->pi_mutex); } /* * Allocate a file number */ void pfs_fileno_alloc(struct pfs_node *pn) { if (pn->pn_parent) PFS_TRACE(("%s/%s", pn->pn_parent->pn_name, pn->pn_name)); else PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); switch (pn->pn_type) { case pfstype_root: /* root must always be 2 */ pn->pn_fileno = 2; break; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: pn->pn_fileno = alloc_unr(pn->pn_info->pi_unrhdr); break; case pfstype_this: KASSERT(pn->pn_parent != NULL, ("%s(): pfstype_this node has no parent", __func__)); pn->pn_fileno = pn->pn_parent->pn_fileno; break; case pfstype_parent: KASSERT(pn->pn_parent != NULL, ("%s(): pfstype_parent node has no parent", __func__)); if (pn->pn_parent->pn_type == pfstype_root) { pn->pn_fileno = pn->pn_parent->pn_fileno; break; } KASSERT(pn->pn_parent->pn_parent != NULL, ("%s(): pfstype_parent node has no grandparent", __func__)); pn->pn_fileno = pn->pn_parent->pn_parent->pn_fileno; break; case pfstype_none: KASSERT(0, ("%s(): pfstype_none node", __func__)); break; } #if 0 printf("%s(): %s: ", __func__, pn->pn_info->pi_name); if (pn->pn_parent) { if (pn->pn_parent->pn_parent) { printf("%s/", pn->pn_parent->pn_parent->pn_name); } printf("%s/", pn->pn_parent->pn_name); } printf("%s -> %d\n", pn->pn_name, pn->pn_fileno); #endif } /* * Release a file number */ void pfs_fileno_free(struct pfs_node *pn) { pfs_assert_not_owned(pn); switch (pn->pn_type) { case pfstype_root: /* not allocated from unrhdr */ return; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: free_unr(pn->pn_info->pi_unrhdr, pn->pn_fileno); break; case pfstype_this: case pfstype_parent: /* ignore these, as they don't "own" their file number */ break; case pfstype_none: KASSERT(0, ("pfs_fileno_free() called for pfstype_none node")); break; } } diff --git a/sys/fs/pseudofs/pseudofs_internal.h b/sys/fs/pseudofs/pseudofs_internal.h index bb2187aa1ce9..cefeba8d0326 100644 --- a/sys/fs/pseudofs/pseudofs_internal.h +++ b/sys/fs/pseudofs/pseudofs_internal.h @@ -1,211 +1,211 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _PSEUDOFS_INTERNAL_H_INCLUDED #define _PSEUDOFS_INTERNAL_H_INCLUDED /* * Sysctl subtree */ SYSCTL_DECL(_vfs_pfs); /* * Vnode data */ struct pfs_vdata { struct pfs_node *pvd_pn; pid_t pvd_pid; struct vnode *pvd_vnode; SLIST_ENTRY(pfs_vdata) pvd_hash; }; /* * Vnode cache */ void pfs_vncache_load (void); void pfs_vncache_unload (void); int pfs_vncache_alloc (struct mount *, struct vnode **, struct pfs_node *, pid_t pid); int pfs_vncache_free (struct vnode *); /* * File number bitmap */ void pfs_fileno_init (struct pfs_info *); void pfs_fileno_uninit (struct pfs_info *); void pfs_fileno_alloc (struct pfs_node *); void pfs_fileno_free (struct pfs_node *); /* * Debugging */ #ifdef PSEUDOFS_TRACE extern int pfs_trace; #define PFS_TRACE(foo) \ do { \ if (pfs_trace) { \ printf("%s(): line %d: ", __func__, __LINE__); \ printf foo ; \ printf("\n"); \ } \ } while (0) #define PFS_RETURN(err) \ do { \ if (pfs_trace) { \ printf("%s(): line %d: returning %d\n", \ __func__, __LINE__, err); \ } \ return (err); \ } while (0) #else #define PFS_TRACE(foo) \ do { /* nothing */ } while (0) #define PFS_RETURN(err) \ return (err) #endif /* * Inline helpers for locking */ static inline void pfs_lock(struct pfs_node *pn) { mtx_lock(&pn->pn_mutex); } static inline void pfs_unlock(struct pfs_node *pn) { mtx_unlock(&pn->pn_mutex); } static inline void pfs_assert_owned(struct pfs_node *pn) { mtx_assert(&pn->pn_mutex, MA_OWNED); } static inline void pfs_assert_not_owned(struct pfs_node *pn) { mtx_assert(&pn->pn_mutex, MA_NOTOWNED); } static inline int pn_fill(PFS_FILL_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_fill != NULL, ("%s(): no callback", __func__)); if (p != NULL) { PROC_LOCK_ASSERT(p, MA_NOTOWNED); PROC_ASSERT_HELD(p); } pfs_assert_not_owned(pn); return ((pn->pn_fill)(PFS_FILL_ARGNAMES)); } static inline int pn_attr(PFS_ATTR_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_attr != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_attr)(PFS_ATTR_ARGNAMES)); } static inline int pn_vis(PFS_VIS_ARGS) { PFS_TRACE(("%s", pn->pn_name)); if (pn->pn_vis == NULL) return (1); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_vis)(PFS_VIS_ARGNAMES)); } static inline int pn_ioctl(PFS_IOCTL_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_ioctl != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_ioctl)(PFS_IOCTL_ARGNAMES)); } static inline int pn_getextattr(PFS_GETEXTATTR_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_getextattr != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_getextattr)(PFS_GETEXTATTR_ARGNAMES)); } static inline int pn_close(PFS_CLOSE_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_close != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_close)(PFS_CLOSE_ARGNAMES)); } static inline int pn_destroy(PFS_DESTROY_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_destroy != NULL, ("%s(): no callback", __func__)); pfs_assert_not_owned(pn); return ((pn->pn_destroy)(PFS_DESTROY_ARGNAMES)); } #endif diff --git a/sys/fs/pseudofs/pseudofs_vncache.c b/sys/fs/pseudofs/pseudofs_vncache.c index 2120398f5b2e..33023dd0d08f 100644 --- a/sys/fs/pseudofs/pseudofs_vncache.c +++ b/sys/fs/pseudofs/pseudofs_vncache.c @@ -1,362 +1,362 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); static struct mtx pfs_vncache_mutex; static eventhandler_tag pfs_exit_tag; static void pfs_exit(void *arg, struct proc *p); static void pfs_purge_all(void); static SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "pseudofs vnode cache"); static int pfs_vncache_entries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, &pfs_vncache_entries, 0, "number of entries in the vnode cache"); static int pfs_vncache_maxentries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, &pfs_vncache_maxentries, 0, "highest number of entries in the vnode cache"); static int pfs_vncache_hits; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, &pfs_vncache_hits, 0, "number of cache hits since initialization"); static int pfs_vncache_misses; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, &pfs_vncache_misses, 0, "number of cache misses since initialization"); extern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ static SLIST_HEAD(pfs_vncache_head, pfs_vdata) *pfs_vncache_hashtbl; static u_long pfs_vncache_hash; #define PFS_VNCACHE_HASH(pid) (&pfs_vncache_hashtbl[(pid) & pfs_vncache_hash]) /* * Initialize vnode cache */ void pfs_vncache_load(void) { mtx_init(&pfs_vncache_mutex, "pfs_vncache", NULL, MTX_DEF); pfs_vncache_hashtbl = hashinit(maxproc / 4, M_PFSVNCACHE, &pfs_vncache_hash); pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, EVENTHANDLER_PRI_ANY); } /* * Tear down vnode cache */ void pfs_vncache_unload(void) { EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); pfs_purge_all(); KASSERT(pfs_vncache_entries == 0, ("%d vncache entries remaining", pfs_vncache_entries)); mtx_destroy(&pfs_vncache_mutex); hashdestroy(pfs_vncache_hashtbl, M_PFSVNCACHE, pfs_vncache_hash); } /* * Allocate a vnode */ int pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, pid_t pid) { struct pfs_vncache_head *hash; struct pfs_vdata *pvd, *pvd2; struct vnode *vp; enum vgetstate vs; int error; /* * See if the vnode is in the cache. */ hash = PFS_VNCACHE_HASH(pid); if (SLIST_EMPTY(hash)) goto alloc; retry: mtx_lock(&pfs_vncache_mutex); SLIST_FOREACH(pvd, hash, pvd_hash) { if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && pvd->pvd_vnode->v_mount == mp) { vp = pvd->pvd_vnode; vs = vget_prep(vp); mtx_unlock(&pfs_vncache_mutex); if (vget_finish(vp, LK_EXCLUSIVE, vs) == 0) { ++pfs_vncache_hits; *vpp = vp; /* * Some callers cache_enter(vp) later, so * we have to make sure it's not in the * VFS cache so it doesn't get entered * twice. A better solution would be to * make pfs_vncache_alloc() responsible * for entering the vnode in the VFS * cache. */ cache_purge(vp); return (0); } goto retry; } } mtx_unlock(&pfs_vncache_mutex); alloc: /* nope, get a new one */ pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK); error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); if (error) { free(pvd, M_PFSVNCACHE); return (error); } pvd->pvd_pn = pn; pvd->pvd_pid = pid; (*vpp)->v_data = pvd; switch (pn->pn_type) { case pfstype_root: (*vpp)->v_vflag = VV_ROOT; #if 0 printf("root vnode allocated\n"); #endif /* fall through */ case pfstype_dir: case pfstype_this: case pfstype_parent: case pfstype_procdir: (*vpp)->v_type = VDIR; break; case pfstype_file: (*vpp)->v_type = VREG; break; case pfstype_symlink: (*vpp)->v_type = VLNK; break; case pfstype_none: KASSERT(0, ("pfs_vncache_alloc called for null node\n")); default: panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); } /* * Propagate flag through to vnode so users know it can change * if the process changes (i.e. execve) */ if ((pn->pn_flags & PFS_PROCDEP) != 0) (*vpp)->v_vflag |= VV_PROCDEP; pvd->pvd_vnode = *vpp; vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); VN_LOCK_AREC(*vpp); error = insmntque(*vpp, mp); if (error != 0) { free(pvd, M_PFSVNCACHE); *vpp = NULLVP; return (error); } vn_set_state(*vpp, VSTATE_CONSTRUCTED); retry2: mtx_lock(&pfs_vncache_mutex); /* * Other thread may race with us, creating the entry we are * going to insert into the cache. Recheck after * pfs_vncache_mutex is reacquired. */ SLIST_FOREACH(pvd2, hash, pvd_hash) { if (pvd2->pvd_pn == pn && pvd2->pvd_pid == pid && pvd2->pvd_vnode->v_mount == mp) { vp = pvd2->pvd_vnode; vs = vget_prep(vp); mtx_unlock(&pfs_vncache_mutex); if (vget_finish(vp, LK_EXCLUSIVE, vs) == 0) { ++pfs_vncache_hits; vgone(*vpp); vput(*vpp); *vpp = vp; cache_purge(vp); return (0); } goto retry2; } } ++pfs_vncache_misses; if (++pfs_vncache_entries > pfs_vncache_maxentries) pfs_vncache_maxentries = pfs_vncache_entries; SLIST_INSERT_HEAD(hash, pvd, pvd_hash); mtx_unlock(&pfs_vncache_mutex); return (0); } /* * Free a vnode */ int pfs_vncache_free(struct vnode *vp) { struct pfs_vdata *pvd, *pvd2; mtx_lock(&pfs_vncache_mutex); pvd = (struct pfs_vdata *)vp->v_data; KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); SLIST_FOREACH(pvd2, PFS_VNCACHE_HASH(pvd->pvd_pid), pvd_hash) { if (pvd2 != pvd) continue; SLIST_REMOVE(PFS_VNCACHE_HASH(pvd->pvd_pid), pvd, pfs_vdata, pvd_hash); --pfs_vncache_entries; break; } mtx_unlock(&pfs_vncache_mutex); free(pvd, M_PFSVNCACHE); vp->v_data = NULL; return (0); } /* * Purge the cache of dead entries * * The code is not very efficient and this perhaps can be addressed without * a complete rewrite. Previous iteration was walking a linked list from * scratch every time. This code only walks the relevant hash chain (if pid * is provided), but still resorts to scanning the entire cache at least twice * if a specific component is to be removed which is slower. This can be * augmented with resizing the hash. * * Explanation of the previous state: * * This is extremely inefficient due to the fact that vgone() not only * indirectly modifies the vnode cache, but may also sleep. We can * neither hold pfs_vncache_mutex across a vgone() call, nor make any * assumptions about the state of the cache after vgone() returns. In * consequence, we must start over after every vgone() call, and keep * trying until we manage to traverse the entire cache. * * The only way to improve this situation is to change the data structure * used to implement the cache. */ static void pfs_purge_one(struct vnode *vnp) { VOP_LOCK(vnp, LK_EXCLUSIVE); vgone(vnp); VOP_UNLOCK(vnp); vdrop(vnp); } void pfs_purge(struct pfs_node *pn) { struct pfs_vdata *pvd; struct vnode *vnp; u_long i, removed; mtx_lock(&pfs_vncache_mutex); restart: removed = 0; for (i = 0; i < pfs_vncache_hash; i++) { restart_chain: SLIST_FOREACH(pvd, &pfs_vncache_hashtbl[i], pvd_hash) { if (pn != NULL && pvd->pvd_pn != pn) continue; vnp = pvd->pvd_vnode; vhold(vnp); mtx_unlock(&pfs_vncache_mutex); pfs_purge_one(vnp); removed++; mtx_lock(&pfs_vncache_mutex); goto restart_chain; } } if (removed > 0) goto restart; mtx_unlock(&pfs_vncache_mutex); } static void pfs_purge_all(void) { pfs_purge(NULL); } /* * Free all vnodes associated with a defunct process */ static void pfs_exit(void *arg, struct proc *p) { struct pfs_vncache_head *hash; struct pfs_vdata *pvd; struct vnode *vnp; int pid; pid = p->p_pid; hash = PFS_VNCACHE_HASH(pid); if (SLIST_EMPTY(hash)) return; restart: mtx_lock(&pfs_vncache_mutex); SLIST_FOREACH(pvd, hash, pvd_hash) { if (pvd->pvd_pid != pid) continue; vnp = pvd->pvd_vnode; vhold(vnp); mtx_unlock(&pfs_vncache_mutex); pfs_purge_one(vnp); goto restart; } mtx_unlock(&pfs_vncache_mutex); } diff --git a/sys/fs/pseudofs/pseudofs_vnops.c b/sys/fs/pseudofs/pseudofs_vnops.c index 48adbfdb4c47..0bdfedffafcb 100644 --- a/sys/fs/pseudofs/pseudofs_vnops.c +++ b/sys/fs/pseudofs/pseudofs_vnops.c @@ -1,1167 +1,1167 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2001 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KASSERT_PN_IS_DIR(pn) \ KASSERT((pn)->pn_type == pfstype_root || \ (pn)->pn_type == pfstype_dir || \ (pn)->pn_type == pfstype_procdir, \ ("%s(): VDIR vnode refers to non-directory pfs_node", __func__)) #define KASSERT_PN_IS_FILE(pn) \ KASSERT((pn)->pn_type == pfstype_file, \ ("%s(): VREG vnode refers to non-file pfs_node", __func__)) #define KASSERT_PN_IS_LINK(pn) \ KASSERT((pn)->pn_type == pfstype_symlink, \ ("%s(): VLNK vnode refers to non-link pfs_node", __func__)) #define PFS_MAXBUFSIZ 1024 * 1024 /* * Returns the fileno, adjusted for target pid */ static uint32_t pn_fileno(struct pfs_node *pn, pid_t pid) { KASSERT(pn->pn_fileno > 0, ("%s(): no fileno allocated", __func__)); if (pid != NO_PID) return (pn->pn_fileno * NO_PID + pid); return (pn->pn_fileno); } /* * Returns non-zero if given file is visible to given thread. */ static int pfs_visible_proc(struct thread *td, struct pfs_node *pn, struct proc *proc) { if (proc == NULL) return (0); PROC_LOCK_ASSERT(proc, MA_OWNED); if ((proc->p_flag & P_WEXIT) != 0) return (0); if (p_cansee(td, proc) != 0) return (0); return (pn_vis(td, proc, pn)); } static int pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid, struct proc **p) { struct proc *proc; PFS_TRACE(("%s (pid: %d, req: %d)", pn->pn_name, pid, td->td_proc->p_pid)); if (p) *p = NULL; if (pid == NO_PID) PFS_RETURN (pn_vis(td, NULL, pn)); proc = pfind(pid); if (proc == NULL) PFS_RETURN (0); if (pfs_visible_proc(td, pn, proc)) { if (p) *p = proc; else PROC_UNLOCK(proc); PFS_RETURN (1); } PROC_UNLOCK(proc); PFS_RETURN (0); } static int pfs_lookup_proc(pid_t pid, struct proc **p) { struct proc *proc; proc = pfind(pid); if (proc == NULL) return (0); if ((proc->p_flag & P_WEXIT) != 0) { PROC_UNLOCK(proc); return (0); } _PHOLD(proc); PROC_UNLOCK(proc); *p = proc; return (1); } /* * Verify permissions */ static int pfs_access(struct vop_access_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct vattr vattr; int error; PFS_TRACE(("%s", pvd->pvd_pn->pn_name)); (void)pvd; error = VOP_GETATTR(vn, &vattr, va->a_cred); if (error) PFS_RETURN (error); error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, vattr.va_gid, va->a_accmode, va->a_cred); PFS_RETURN (error); } /* * Close a file or directory */ static int pfs_close(struct vop_close_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct proc *proc; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); /* * Do nothing unless this is the last close and the node has a * last-close handler. */ if (vrefcnt(vn) > 1 || pn->pn_close == NULL) PFS_RETURN (0); if (pvd->pvd_pid != NO_PID) { proc = pfind(pvd->pvd_pid); } else { proc = NULL; } error = pn_close(va->a_td, proc, pn); if (proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Get file attributes */ static int pfs_getattr(struct vop_getattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct vattr *vap = va->a_vap; struct proc *proc; int error = 0; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (ENOENT); vap->va_type = vn->v_type; vap->va_fileid = pn_fileno(pn, pvd->pvd_pid); vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; vap->va_filerev = 0; vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; vap->va_nlink = 1; nanotime(&vap->va_ctime); vap->va_atime = vap->va_mtime = vap->va_ctime; switch (pn->pn_type) { case pfstype_procdir: case pfstype_root: case pfstype_dir: #if 0 pfs_lock(pn); /* compute link count */ pfs_unlock(pn); #endif vap->va_mode = 0555; break; case pfstype_file: case pfstype_symlink: vap->va_mode = 0444; break; default: printf("shouldn't be here!\n"); vap->va_mode = 0; break; } if (proc != NULL) { vap->va_uid = proc->p_ucred->cr_ruid; vap->va_gid = proc->p_ucred->cr_rgid; } else { vap->va_uid = 0; vap->va_gid = 0; } if (pn->pn_attr != NULL) error = pn_attr(curthread, proc, pn, vap); if(proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Perform an ioctl */ static int pfs_ioctl(struct vop_ioctl_args *va) { struct vnode *vn; struct pfs_vdata *pvd; struct pfs_node *pn; struct proc *proc; int error; vn = va->a_vp; vn_lock(vn, LK_SHARED | LK_RETRY); if (VN_IS_DOOMED(vn)) { VOP_UNLOCK(vn); return (EBADF); } pvd = vn->v_data; pn = pvd->pvd_pn; PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) { VOP_UNLOCK(vn); PFS_RETURN (EINVAL); } KASSERT_PN_IS_FILE(pn); if (pn->pn_ioctl == NULL) { VOP_UNLOCK(vn); PFS_RETURN (ENOTTY); } /* * This is necessary because process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) { VOP_UNLOCK(vn); PFS_RETURN (EIO); } error = pn_ioctl(curthread, proc, pn, va->a_command, va->a_data); if (proc != NULL) PROC_UNLOCK(proc); VOP_UNLOCK(vn); PFS_RETURN (error); } /* * Perform getextattr */ static int pfs_getextattr(struct vop_getextattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct proc *proc; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (pn->pn_getextattr == NULL) error = EOPNOTSUPP; else error = pn_getextattr(curthread, proc, pn, va->a_attrnamespace, va->a_name, va->a_uio, va->a_size, va->a_cred); if (proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Convert a vnode to its component name */ static int pfs_vptocnp(struct vop_vptocnp_args *ap) { struct vnode *vp = ap->a_vp; struct vnode **dvp = ap->a_vpp; struct pfs_vdata *pvd = vp->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn; struct mount *mp; char *buf = ap->a_buf; size_t *buflen = ap->a_buflen; char pidbuf[PFS_NAMELEN]; pid_t pid = pvd->pvd_pid; int len, i, error, locked; i = *buflen; error = 0; pfs_lock(pd); if (vp->v_type == VDIR && pd->pn_type == pfstype_root) { *dvp = vp; vhold(*dvp); pfs_unlock(pd); PFS_RETURN (0); } else if (vp->v_type == VDIR && pd->pn_type == pfstype_procdir) { len = snprintf(pidbuf, sizeof(pidbuf), "%d", pid); i -= len; if (i < 0) { error = ENOMEM; goto failed; } bcopy(pidbuf, buf + i, len); } else { len = strlen(pd->pn_name); i -= len; if (i < 0) { error = ENOMEM; goto failed; } bcopy(pd->pn_name, buf + i, len); } pn = pd->pn_parent; pfs_unlock(pd); mp = vp->v_mount; error = vfs_busy(mp, 0); if (error) return (error); /* * vp is held by caller. */ locked = VOP_ISLOCKED(vp); VOP_UNLOCK(vp); error = pfs_vncache_alloc(mp, dvp, pn, pid); if (error) { vn_lock(vp, locked | LK_RETRY); vfs_unbusy(mp); PFS_RETURN(error); } *buflen = i; VOP_UNLOCK(*dvp); vn_lock(vp, locked | LK_RETRY); vfs_unbusy(mp); PFS_RETURN (0); failed: pfs_unlock(pd); PFS_RETURN(error); } /* * Look up a file or directory */ static int pfs_lookup(struct vop_cachedlookup_args *va) { struct vnode *vn = va->a_dvp; struct vnode **vpp = va->a_vpp; struct componentname *cnp = va->a_cnp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn, *pdn = NULL; struct mount *mp; pid_t pid = pvd->pvd_pid; char *pname; int error, i, namelen, visible; PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr)); pfs_assert_not_owned(pd); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); KASSERT_PN_IS_DIR(pd); /* * Don't support DELETE or RENAME. CREATE is supported so * that O_CREAT will work, but the lookup will still fail if * the file does not exist. */ if ((cnp->cn_flags & ISLASTCN) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) PFS_RETURN (EOPNOTSUPP); /* shortcut: check if the name is too long */ if (cnp->cn_namelen >= PFS_NAMELEN) PFS_RETURN (ENOENT); /* check that parent directory is visible... */ if (!pfs_visible(curthread, pd, pvd->pvd_pid, NULL)) PFS_RETURN (ENOENT); /* self */ namelen = cnp->cn_namelen; pname = cnp->cn_nameptr; if (namelen == 1 && pname[0] == '.') { pn = pd; *vpp = vn; VREF(vn); PFS_RETURN (0); } mp = vn->v_mount; /* parent */ if (cnp->cn_flags & ISDOTDOT) { if (pd->pn_type == pfstype_root) PFS_RETURN (EIO); error = vfs_busy(mp, MBF_NOWAIT); if (error != 0) { vfs_ref(mp); VOP_UNLOCK(vn); error = vfs_busy(mp, 0); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); vfs_rel(mp); if (error != 0) PFS_RETURN(ENOENT); if (VN_IS_DOOMED(vn)) { vfs_unbusy(mp); PFS_RETURN(ENOENT); } } VOP_UNLOCK(vn); KASSERT(pd->pn_parent != NULL, ("%s(): non-root directory has no parent", __func__)); /* * This one is tricky. Descendents of procdir nodes * inherit their parent's process affinity, but * there's no easy reverse mapping. For simplicity, * we assume that if this node is a procdir, its * parent isn't (which is correct as long as * descendents of procdir nodes are never procdir * nodes themselves) */ if (pd->pn_type == pfstype_procdir) pid = NO_PID; pfs_lock(pd); pn = pd->pn_parent; pfs_unlock(pd); goto got_pnode; } pfs_lock(pd); /* named node */ for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next) if (pn->pn_type == pfstype_procdir) pdn = pn; else if (strncmp(pname, pn->pn_name, namelen) == 0 && pn->pn_name[namelen] == '\0') { pfs_unlock(pd); goto got_pnode; } /* process dependent node */ if ((pn = pdn) != NULL) { pid = 0; for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i) if ((pid = pid * 10 + pname[i] - '0') > PID_MAX) break; if (i == cnp->cn_namelen) { pfs_unlock(pd); goto got_pnode; } } pfs_unlock(pd); PFS_RETURN (ENOENT); got_pnode: pfs_assert_not_owned(pd); pfs_assert_not_owned(pn); visible = pfs_visible(curthread, pn, pid, NULL); if (!visible) { error = ENOENT; goto failed; } error = pfs_vncache_alloc(mp, vpp, pn, pid); if (error) goto failed; if (cnp->cn_flags & ISDOTDOT) { vfs_unbusy(mp); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); if (VN_IS_DOOMED(vn)) { vput(*vpp); *vpp = NULL; PFS_RETURN(ENOENT); } } if (cnp->cn_flags & MAKEENTRY && !VN_IS_DOOMED(vn)) cache_enter(vn, *vpp, cnp); PFS_RETURN (0); failed: if (cnp->cn_flags & ISDOTDOT) { vfs_unbusy(mp); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); *vpp = NULL; } PFS_RETURN(error); } /* * Open a file or directory. */ static int pfs_open(struct vop_open_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; int mode = va->a_mode; PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode)); pfs_assert_not_owned(pn); /* check if the requested mode is permitted */ if (((mode & FREAD) && !(mode & PFS_RD)) || ((mode & FWRITE) && !(mode & PFS_WR))) PFS_RETURN (EPERM); /* we don't support locking */ if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) PFS_RETURN (EOPNOTSUPP); PFS_RETURN (0); } struct sbuf_seek_helper { off_t skip_bytes; struct uio *uio; }; static int pfs_sbuf_uio_drain(void *arg, const char *data, int len) { struct sbuf_seek_helper *ssh; struct uio *uio; int error, skipped; ssh = arg; uio = ssh->uio; skipped = 0; /* Need to discard first uio_offset bytes. */ if (ssh->skip_bytes > 0) { if (ssh->skip_bytes >= len) { ssh->skip_bytes -= len; return (len); } data += ssh->skip_bytes; len -= ssh->skip_bytes; skipped = ssh->skip_bytes; ssh->skip_bytes = 0; } error = uiomove(__DECONST(void *, data), len, uio); if (error != 0) return (-error); /* * The fill function has more to emit, but the reader is finished. * This is similar to the truncated read case for non-draining PFS * sbufs, and should be handled appropriately in fill-routines. */ if (uio->uio_resid == 0) return (-ENOBUFS); return (skipped + len); } /* * Read from a file */ static int pfs_read(struct vop_read_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc; struct sbuf *sb = NULL; int error, locked; off_t buflen, buflim; struct sbuf_seek_helper ssh; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) PFS_RETURN (EINVAL); KASSERT_PN_IS_FILE(pn); if (!(pn->pn_flags & PFS_RD)) PFS_RETURN (EBADF); if (pn->pn_fill == NULL) PFS_RETURN (EIO); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (proc != NULL) { _PHOLD(proc); PROC_UNLOCK(proc); } vhold(vn); locked = VOP_ISLOCKED(vn); VOP_UNLOCK(vn); if (pn->pn_flags & PFS_RAWRD) { PFS_TRACE(("%zd resid", uio->uio_resid)); error = pn_fill(curthread, proc, pn, NULL, uio); PFS_TRACE(("%zd resid", uio->uio_resid)); goto ret; } if (uio->uio_resid < 0 || uio->uio_offset < 0 || uio->uio_resid > OFF_MAX - uio->uio_offset) { error = EINVAL; goto ret; } buflen = uio->uio_offset + uio->uio_resid + 1; if (pn->pn_flags & PFS_AUTODRAIN) /* * We can use a smaller buffer if we can stream output to the * consumer. */ buflim = PAGE_SIZE; else buflim = PFS_MAXBUFSIZ; if (buflen > buflim) buflen = buflim; sb = sbuf_new(sb, NULL, buflen, 0); if (sb == NULL) { error = EIO; goto ret; } if (pn->pn_flags & PFS_AUTODRAIN) { ssh.skip_bytes = uio->uio_offset; ssh.uio = uio; sbuf_set_drain(sb, pfs_sbuf_uio_drain, &ssh); } error = pn_fill(curthread, proc, pn, sb, uio); if (error) { sbuf_delete(sb); goto ret; } /* * XXX: If the buffer overflowed, sbuf_len() will not return * the data length. Then just use the full length because an * overflowed sbuf must be full. */ error = sbuf_finish(sb); if ((pn->pn_flags & PFS_AUTODRAIN)) { /* * ENOBUFS just indicates early termination of the fill * function as the caller's buffer was already filled. Squash * to zero. */ if (uio->uio_resid == 0 && error == ENOBUFS) error = 0; } else { if (error == 0) buflen = sbuf_len(sb); else /* The trailing byte is not valid. */ buflen--; error = uiomove_frombuf(sbuf_data(sb), buflen, uio); } sbuf_delete(sb); ret: vn_lock(vn, locked | LK_RETRY); vdrop(vn); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } /* * Iterate through directory entries */ static int pfs_iterate(struct thread *td, struct proc *proc, struct pfs_node *pd, struct pfs_node **pn, struct proc **p) { int visible; sx_assert(&allproc_lock, SX_SLOCKED); pfs_assert_owned(pd); again: if (*pn == NULL) { /* first node */ *pn = pd->pn_nodes; } else if ((*pn)->pn_type != pfstype_procdir) { /* next node */ *pn = (*pn)->pn_next; } if (*pn != NULL && (*pn)->pn_type == pfstype_procdir) { /* next process */ if (*p == NULL) *p = LIST_FIRST(&allproc); else *p = LIST_NEXT(*p, p_list); /* out of processes: next node */ if (*p == NULL) *pn = (*pn)->pn_next; else PROC_LOCK(*p); } if ((*pn) == NULL) return (-1); if (*p != NULL) { visible = pfs_visible_proc(td, *pn, *p); PROC_UNLOCK(*p); } else if (proc != NULL) { visible = pfs_visible_proc(td, *pn, proc); } else { visible = pn_vis(td, NULL, *pn); } if (!visible) goto again; return (0); } /* Directory entry list */ struct pfsentry { STAILQ_ENTRY(pfsentry) link; struct dirent entry; }; STAILQ_HEAD(pfsdirentlist, pfsentry); /* * Return directory entries. */ static int pfs_readdir(struct vop_readdir_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pd = pvd->pvd_pn; pid_t pid = pvd->pvd_pid; struct proc *p, *proc; struct pfs_node *pn; struct uio *uio; struct pfsentry *pfsent, *pfsent2; struct pfsdirentlist lst; off_t offset; int error, i, resid; STAILQ_INIT(&lst); error = 0; KASSERT(pd->pn_info == vn->v_mount->mnt_data, ("%s(): pn_info does not match mountpoint", __func__)); PFS_TRACE(("%s pid %lu", pd->pn_name, (unsigned long)pid)); pfs_assert_not_owned(pd); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); KASSERT_PN_IS_DIR(pd); uio = va->a_uio; /* only allow reading entire entries */ offset = uio->uio_offset; resid = uio->uio_resid; if (offset < 0 || offset % PFS_DELEN != 0 || (resid && resid < PFS_DELEN)) PFS_RETURN (EINVAL); if (resid == 0) PFS_RETURN (0); proc = NULL; if (pid != NO_PID && !pfs_lookup_proc(pid, &proc)) PFS_RETURN (ENOENT); sx_slock(&allproc_lock); pfs_lock(pd); KASSERT(pid == NO_PID || proc != NULL, ("%s(): no process for pid %lu", __func__, (unsigned long)pid)); if (pid != NO_PID) { PROC_LOCK(proc); /* check if the directory is visible to the caller */ if (!pfs_visible_proc(curthread, pd, proc)) { _PRELE(proc); PROC_UNLOCK(proc); pfs_unlock(pd); sx_sunlock(&allproc_lock); PFS_RETURN (ENOENT); } } /* skip unwanted entries */ for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN) { if (pfs_iterate(curthread, proc, pd, &pn, &p) == -1) { /* nothing left... */ if (proc != NULL) { _PRELE(proc); PROC_UNLOCK(proc); } pfs_unlock(pd); sx_sunlock(&allproc_lock); PFS_RETURN (0); } } /* fill in entries */ while (pfs_iterate(curthread, proc, pd, &pn, &p) != -1 && resid >= PFS_DELEN) { if ((pfsent = malloc(sizeof(struct pfsentry), M_IOV, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; break; } pfsent->entry.d_reclen = PFS_DELEN; pfsent->entry.d_fileno = pn_fileno(pn, pid); /* PFS_DELEN was picked to fit PFS_NAMLEN */ for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i) pfsent->entry.d_name[i] = pn->pn_name[i]; pfsent->entry.d_namlen = i; /* NOTE: d_off is the offset of the *next* entry. */ pfsent->entry.d_off = offset + PFS_DELEN; switch (pn->pn_type) { case pfstype_procdir: KASSERT(p != NULL, ("reached procdir node with p == NULL")); pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name, PFS_NAMELEN, "%d", p->p_pid); /* fall through */ case pfstype_root: case pfstype_dir: case pfstype_this: case pfstype_parent: pfsent->entry.d_type = DT_DIR; break; case pfstype_file: pfsent->entry.d_type = DT_REG; break; case pfstype_symlink: pfsent->entry.d_type = DT_LNK; break; default: panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type); } PFS_TRACE(("%s", pfsent->entry.d_name)); dirent_terminate(&pfsent->entry); STAILQ_INSERT_TAIL(&lst, pfsent, link); offset += PFS_DELEN; resid -= PFS_DELEN; } if (proc != NULL) { _PRELE(proc); PROC_UNLOCK(proc); } pfs_unlock(pd); sx_sunlock(&allproc_lock); i = 0; STAILQ_FOREACH_SAFE(pfsent, &lst, link, pfsent2) { if (error == 0) error = uiomove(&pfsent->entry, PFS_DELEN, uio); free(pfsent, M_IOV); i++; } PFS_TRACE(("%ju bytes", (uintmax_t)(i * PFS_DELEN))); PFS_RETURN (error); } /* * Read a symbolic link */ static int pfs_readlink(struct vop_readlink_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc = NULL; char buf[PATH_MAX]; struct sbuf sb; int error, locked; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (vn->v_type != VLNK) PFS_RETURN (EINVAL); KASSERT_PN_IS_LINK(pn); if (pn->pn_fill == NULL) PFS_RETURN (EIO); if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (EIO); if (proc->p_flag & P_WEXIT) { PROC_UNLOCK(proc); PFS_RETURN (EIO); } _PHOLD(proc); PROC_UNLOCK(proc); } vhold(vn); locked = VOP_ISLOCKED(vn); VOP_UNLOCK(vn); /* sbuf_new() can't fail with a static buffer */ sbuf_new(&sb, buf, sizeof buf, 0); error = pn_fill(curthread, proc, pn, &sb, NULL); if (proc != NULL) PRELE(proc); vn_lock(vn, locked | LK_RETRY); vdrop(vn); if (error) { sbuf_delete(&sb); PFS_RETURN (error); } if (sbuf_finish(&sb) != 0) { sbuf_delete(&sb); PFS_RETURN (ENAMETOOLONG); } error = uiomove_frombuf(sbuf_data(&sb), sbuf_len(&sb), uio); sbuf_delete(&sb); PFS_RETURN (error); } /* * Reclaim a vnode */ static int pfs_reclaim(struct vop_reclaim_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); return (pfs_vncache_free(va->a_vp)); } /* * Set attributes */ static int pfs_setattr(struct vop_setattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); /* Silently ignore unchangeable attributes. */ PFS_RETURN (0); } /* * Write to a file */ static int pfs_write(struct vop_write_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc; struct sbuf sb; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) PFS_RETURN (EINVAL); KASSERT_PN_IS_FILE(pn); if (!(pn->pn_flags & PFS_WR)) PFS_RETURN (EBADF); if (pn->pn_fill == NULL) PFS_RETURN (EIO); if (uio->uio_resid > PFS_MAXBUFSIZ) PFS_RETURN (EIO); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (proc != NULL) { _PHOLD(proc); PROC_UNLOCK(proc); } if (pn->pn_flags & PFS_RAWWR) { error = pn_fill(curthread, proc, pn, NULL, uio); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } sbuf_uionew(&sb, uio, &error); if (error) { if (proc != NULL) PRELE(proc); PFS_RETURN (error); } error = pn_fill(curthread, proc, pn, &sb, uio); sbuf_delete(&sb); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } /* * Vnode operations */ struct vop_vector pfs_vnodeops = { .vop_default = &default_vnodeops, .vop_access = pfs_access, .vop_cachedlookup = pfs_lookup, .vop_close = pfs_close, .vop_create = VOP_EOPNOTSUPP, .vop_getattr = pfs_getattr, .vop_getextattr = pfs_getextattr, .vop_ioctl = pfs_ioctl, .vop_link = VOP_EOPNOTSUPP, .vop_lookup = vfs_cache_lookup, .vop_mkdir = VOP_EOPNOTSUPP, .vop_mknod = VOP_EOPNOTSUPP, .vop_open = pfs_open, .vop_read = pfs_read, .vop_readdir = pfs_readdir, .vop_readlink = pfs_readlink, .vop_reclaim = pfs_reclaim, .vop_remove = VOP_EOPNOTSUPP, .vop_rename = VOP_EOPNOTSUPP, .vop_rmdir = VOP_EOPNOTSUPP, .vop_setattr = pfs_setattr, .vop_symlink = VOP_EOPNOTSUPP, .vop_vptocnp = pfs_vptocnp, .vop_write = pfs_write, .vop_add_writecount = vop_stdadd_writecount_nomsync, /* XXX I've probably forgotten a few that need VOP_EOPNOTSUPP */ }; VFS_VOP_VECTOR_REGISTER(pfs_vnodeops); diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c index 1d361dcc1391..0ce2427f6dbb 100644 --- a/sys/kern/subr_sbuf.c +++ b/sys/kern/subr_sbuf.c @@ -1,975 +1,975 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000-2008 Poul-Henning Kamp - * Copyright (c) 2000-2008 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2000-2008 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #else /* _KERNEL */ #include #include #include #include #include #include #include #endif /* _KERNEL */ #include #ifdef _KERNEL static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); #define SBMALLOC(size, flags) malloc(size, M_SBUF, (flags) | M_ZERO) #define SBFREE(buf) free(buf, M_SBUF) #else /* _KERNEL */ #define KASSERT(e, m) #define SBMALLOC(size, flags) calloc(1, size) #define SBFREE(buf) free(buf) #endif /* _KERNEL */ /* * Predicates */ #define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) #define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT) #define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED) #define SBUF_ISDRAINATEOL(s) ((s)->s_flags & SBUF_DRAINATEOL) #define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1) #define SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1)) #define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND) #define SBUF_ISSECTION(s) ((s)->s_flags & SBUF_INSECTION) #define SBUF_NULINCLUDED(s) ((s)->s_flags & SBUF_INCLUDENUL) #define SBUF_ISDRAINTOEOR(s) ((s)->s_flags & SBUF_DRAINTOEOR) #define SBUF_DODRAINTOEOR(s) (SBUF_ISSECTION(s) && SBUF_ISDRAINTOEOR(s)) #define SBUF_MALLOCFLAG(s) \ (((s)->s_flags & SBUF_NOWAIT) ? M_NOWAIT : M_WAITOK) /* * Set / clear flags */ #define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) #define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0) #define SBUF_MINSIZE 2 /* Min is 1 byte + nulterm. */ #define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */ #ifdef PAGE_SIZE #define SBUF_MAXEXTENDSIZE PAGE_SIZE #define SBUF_MAXEXTENDINCR PAGE_SIZE #else #define SBUF_MAXEXTENDSIZE 4096 #define SBUF_MAXEXTENDINCR 4096 #endif /* * Debugging support */ #if defined(_KERNEL) && defined(INVARIANTS) static void _assert_sbuf_integrity(const char *fun, struct sbuf *s) { KASSERT(s != NULL, ("%s called with a NULL sbuf pointer", fun)); KASSERT(s->s_buf != NULL, ("%s called with uninitialized or corrupt sbuf", fun)); if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) { KASSERT(s->s_len <= s->s_size, ("wrote past end of sbuf (%jd >= %jd)", (intmax_t)s->s_len, (intmax_t)s->s_size)); } else { KASSERT(s->s_len < s->s_size, ("wrote past end of sbuf (%jd >= %jd)", (intmax_t)s->s_len, (intmax_t)s->s_size)); } } static void _assert_sbuf_state(const char *fun, struct sbuf *s, int state) { KASSERT((s->s_flags & SBUF_FINISHED) == state, ("%s called with %sfinished or corrupt sbuf", fun, (state ? "un" : ""))); } #define assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s)) #define assert_sbuf_state(s, i) _assert_sbuf_state(__func__, (s), (i)) #else /* _KERNEL && INVARIANTS */ #define assert_sbuf_integrity(s) do { } while (0) #define assert_sbuf_state(s, i) do { } while (0) #endif /* _KERNEL && INVARIANTS */ #ifdef CTASSERT CTASSERT(powerof2(SBUF_MAXEXTENDSIZE)); CTASSERT(powerof2(SBUF_MAXEXTENDINCR)); #endif static int sbuf_extendsize(int size) { int newsize; if (size < (int)SBUF_MAXEXTENDSIZE) { newsize = SBUF_MINEXTENDSIZE; while (newsize < size) newsize *= 2; } else { newsize = roundup2(size, SBUF_MAXEXTENDINCR); } KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size)); return (newsize); } /* * Extend an sbuf. */ static int sbuf_extend(struct sbuf *s, int addlen) { char *newbuf; int newsize; if (!SBUF_CANEXTEND(s)) return (-1); newsize = sbuf_extendsize(s->s_size + addlen); newbuf = SBMALLOC(newsize, SBUF_MALLOCFLAG(s)); if (newbuf == NULL) return (-1); memcpy(newbuf, s->s_buf, s->s_size); if (SBUF_ISDYNAMIC(s)) SBFREE(s->s_buf); else SBUF_SETFLAG(s, SBUF_DYNAMIC); s->s_buf = newbuf; s->s_size = newsize; return (0); } /* * Initialize an sbuf. * If buf is non-NULL, it points to a static or already-allocated string * big enough to hold at least length characters. */ struct sbuf * sbuf_new(struct sbuf *s, char *buf, int length, int flags) { KASSERT(length >= 0, ("attempt to create an sbuf of negative length (%d)", length)); KASSERT((flags & ~SBUF_USRFLAGMSK) == 0, ("%s called with invalid flags", __func__)); KASSERT((flags & SBUF_AUTOEXTEND) || length >= SBUF_MINSIZE, ("sbuf buffer %d smaller than minimum %d bytes", length, SBUF_MINSIZE)); flags &= SBUF_USRFLAGMSK; /* * Allocate 'DYNSTRUCT' sbuf from the heap, if NULL 's' was provided. */ if (s == NULL) { s = SBMALLOC(sizeof(*s), (flags & SBUF_NOWAIT) ? M_NOWAIT : M_WAITOK); if (s == NULL) goto out; SBUF_SETFLAG(s, SBUF_DYNSTRUCT); } else { /* * DYNSTRUCT SBMALLOC sbufs are allocated with M_ZERO, but * user-provided sbuf objects must be initialized. */ memset(s, 0, sizeof(*s)); } s->s_flags |= flags; s->s_size = length; s->s_buf = buf; /* * Never-written sbufs do not need \n termination. */ SBUF_SETFLAG(s, SBUF_DRAINATEOL); /* * Allocate DYNAMIC, i.e., heap data buffer backing the sbuf, if no * buffer was provided. */ if (s->s_buf == NULL) { if (SBUF_CANEXTEND(s)) s->s_size = sbuf_extendsize(s->s_size); s->s_buf = SBMALLOC(s->s_size, SBUF_MALLOCFLAG(s)); if (s->s_buf == NULL) goto out; SBUF_SETFLAG(s, SBUF_DYNAMIC); } out: if (s != NULL && s->s_buf == NULL) { if (SBUF_ISDYNSTRUCT(s)) SBFREE(s); s = NULL; } return (s); } #ifdef _KERNEL /* * Create an sbuf with uio data */ struct sbuf * sbuf_uionew(struct sbuf *s, struct uio *uio, int *error) { KASSERT(uio != NULL, ("%s called with NULL uio pointer", __func__)); KASSERT(error != NULL, ("%s called with NULL error pointer", __func__)); if (uio->uio_resid >= INT_MAX || uio->uio_resid < SBUF_MINSIZE - 1) { *error = EINVAL; return (NULL); } s = sbuf_new(s, NULL, uio->uio_resid + 1, 0); if (s == NULL) { *error = ENOMEM; return (NULL); } *error = uiomove(s->s_buf, uio->uio_resid, uio); if (*error != 0) { sbuf_delete(s); return (NULL); } s->s_len = s->s_size - 1; if (SBUF_ISSECTION(s)) s->s_sect_len = s->s_size - 1; *error = 0; return (s); } #endif int sbuf_get_flags(struct sbuf *s) { return (s->s_flags & SBUF_USRFLAGMSK); } void sbuf_clear_flags(struct sbuf *s, int flags) { s->s_flags &= ~(flags & SBUF_USRFLAGMSK); } void sbuf_set_flags(struct sbuf *s, int flags) { s->s_flags |= (flags & SBUF_USRFLAGMSK); } /* * Clear an sbuf and reset its position. */ void sbuf_clear(struct sbuf *s) { assert_sbuf_integrity(s); /* don't care if it's finished or not */ KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); SBUF_CLEARFLAG(s, SBUF_FINISHED); s->s_error = 0; s->s_len = 0; s->s_rec_off = 0; s->s_sect_len = 0; } /* * Set the sbuf's end position to an arbitrary value. * Effectively truncates the sbuf at the new position. */ int sbuf_setpos(struct sbuf *s, ssize_t pos) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(pos >= 0, ("attempt to seek to a negative position (%jd)", (intmax_t)pos)); KASSERT(pos < s->s_size, ("attempt to seek past end of sbuf (%jd >= %jd)", (intmax_t)pos, (intmax_t)s->s_size)); KASSERT(!SBUF_ISSECTION(s), ("attempt to seek when in a section")); if (pos < 0 || pos > s->s_len) return (-1); s->s_len = pos; return (0); } /* * Drain into a counter. Counts amount of data without producing output. * Useful for cases like sysctl, where user may first request only size. * This allows to avoid pointless allocation/freeing of large buffers. */ int sbuf_count_drain(void *arg, const char *data __unused, int len) { size_t *sizep; sizep = (size_t *)arg; *sizep += len; return (len); } /* * Set up a drain function and argument on an sbuf to flush data to * when the sbuf buffer overflows. */ void sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx) { assert_sbuf_state(s, 0); assert_sbuf_integrity(s); KASSERT(func == s->s_drain_func || s->s_len == 0, ("Cannot change drain to %p on non-empty sbuf %p", func, s)); s->s_drain_func = func; s->s_drain_arg = ctx; } /* * Call the drain and process the return. */ int sbuf_drain(struct sbuf *s) { int len; /* * Immediately return when no work to do, * or an error has already been accumulated. */ if ((s->s_len == 0) || (s->s_error != 0)) return(s->s_error); if (SBUF_DODRAINTOEOR(s) && s->s_rec_off == 0) return (s->s_error = EDEADLK); len = s->s_drain_func(s->s_drain_arg, s->s_buf, SBUF_DODRAINTOEOR(s) ? s->s_rec_off : s->s_len); if (len <= 0) { s->s_error = len ? -len : EDEADLK; return (s->s_error); } KASSERT(len > 0 && len <= s->s_len, ("Bad drain amount %d for sbuf %p", len, s)); s->s_len -= len; s->s_rec_off -= len; /* * Fast path for the expected case where all the data was * drained. */ if (s->s_len == 0) { /* * When the s_buf is entirely drained, we need to remember if * the last character was a '\n' or not for * sbuf_nl_terminate(). */ if (s->s_buf[len - 1] == '\n') SBUF_SETFLAG(s, SBUF_DRAINATEOL); else SBUF_CLEARFLAG(s, SBUF_DRAINATEOL); return (0); } /* * Move the remaining characters to the beginning of the * string. */ memmove(s->s_buf, s->s_buf + len, s->s_len); return (0); } /* * Append bytes to an sbuf. This is the core function for appending * to an sbuf and is the main place that deals with extending the * buffer and marking overflow. */ static void sbuf_put_bytes(struct sbuf *s, const char *buf, size_t len) { size_t n; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); if (s->s_error != 0) return; while (len > 0) { if (SBUF_FREESPACE(s) <= 0) { /* * If there is a drain, use it, otherwise extend the * buffer. */ if (s->s_drain_func != NULL) (void)sbuf_drain(s); else if (sbuf_extend(s, len > INT_MAX ? INT_MAX : len) < 0) s->s_error = ENOMEM; if (s->s_error != 0) return; } n = SBUF_FREESPACE(s); if (len < n) n = len; memcpy(&s->s_buf[s->s_len], buf, n); s->s_len += n; if (SBUF_ISSECTION(s)) s->s_sect_len += n; len -= n; buf += n; } } static void sbuf_put_byte(struct sbuf *s, char c) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); if (__predict_false(s->s_error != 0)) return; if (__predict_false(SBUF_FREESPACE(s) <= 0)) { /* * If there is a drain, use it, otherwise extend the * buffer. */ if (s->s_drain_func != NULL) (void)sbuf_drain(s); else if (sbuf_extend(s, 1) < 0) s->s_error = ENOMEM; if (s->s_error != 0) return; } s->s_buf[s->s_len++] = c; if (SBUF_ISSECTION(s)) s->s_sect_len++; } /* * Append a byte string to an sbuf. */ int sbuf_bcat(struct sbuf *s, const void *buf, size_t len) { sbuf_put_bytes(s, buf, len); if (s->s_error != 0) return (-1); return (0); } #ifdef _KERNEL /* * Copy a byte string from userland into an sbuf. */ int sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("Nonsensical copyin to sbuf %p with a drain", s)); if (s->s_error != 0) return (-1); if (len == 0) return (0); if (len > SBUF_FREESPACE(s)) { sbuf_extend(s, len - SBUF_FREESPACE(s)); if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); } if (copyin(uaddr, s->s_buf + s->s_len, len) != 0) return (-1); s->s_len += len; return (0); } #endif /* * Copy a byte string into an sbuf. */ int sbuf_bcpy(struct sbuf *s, const void *buf, size_t len) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); sbuf_clear(s); return (sbuf_bcat(s, buf, len)); } /* * Append a string to an sbuf. */ int sbuf_cat(struct sbuf *s, const char *str) { size_t n; n = strlen(str); sbuf_put_bytes(s, str, n); if (s->s_error != 0) return (-1); return (0); } #ifdef _KERNEL /* * Append a string from userland to an sbuf. */ int sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len) { size_t done; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("Nonsensical copyin to sbuf %p with a drain", s)); if (s->s_error != 0) return (-1); if (len == 0) len = SBUF_FREESPACE(s); /* XXX return 0? */ if (len > SBUF_FREESPACE(s)) { sbuf_extend(s, len); if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); } switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) { case ENAMETOOLONG: s->s_error = ENOMEM; /* fall through */ case 0: s->s_len += done - 1; if (SBUF_ISSECTION(s)) s->s_sect_len += done - 1; break; default: return (-1); /* XXX */ } return (done); } #endif /* * Copy a string into an sbuf. */ int sbuf_cpy(struct sbuf *s, const char *str) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); sbuf_clear(s); return (sbuf_cat(s, str)); } /* * Format the given argument list and append the resulting string to an sbuf. */ #ifdef _KERNEL /* * Append a non-NUL character to an sbuf. This prototype signature is * suitable for use with kvprintf(9). */ static void sbuf_putc_func(int c, void *arg) { if (__predict_true(c != '\0')) sbuf_put_byte(arg, c); } int sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(fmt != NULL, ("%s called with a NULL format string", __func__)); (void)kvprintf(fmt, sbuf_putc_func, s, 10, ap); if (s->s_error != 0) return (-1); return (0); } #else /* !_KERNEL */ int sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) { va_list ap_copy; int error, len; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(fmt != NULL, ("%s called with a NULL format string", __func__)); if (s->s_error != 0) return (-1); /* * For the moment, there is no way to get vsnprintf(3) to hand * back a character at a time, to push everything into * sbuf_putc_func() as was done for the kernel. * * In userspace, while drains are useful, there's generally * not a problem attempting to malloc(3) on out of space. So * expand a userland sbuf if there is not enough room for the * data produced by sbuf_[v]printf(3). */ error = 0; do { va_copy(ap_copy, ap); len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1, fmt, ap_copy); if (len < 0) { s->s_error = errno; return (-1); } va_end(ap_copy); if (SBUF_FREESPACE(s) >= len) break; /* Cannot print with the current available space. */ if (s->s_drain_func != NULL && s->s_len > 0) error = sbuf_drain(s); /* sbuf_drain() sets s_error. */ else if (sbuf_extend(s, len - SBUF_FREESPACE(s)) != 0) s->s_error = error = ENOMEM; } while (error == 0); /* * s->s_len is the length of the string, without the terminating nul. * When updating s->s_len, we must subtract 1 from the length that * we passed into vsnprintf() because that length includes the * terminating nul. * * vsnprintf() returns the amount that would have been copied, * given sufficient space, so don't over-increment s_len. */ if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); s->s_len += len; if (SBUF_ISSECTION(s)) s->s_sect_len += len; KASSERT(s->s_len < s->s_size, ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); if (s->s_error != 0) return (-1); return (0); } #endif /* _KERNEL */ /* * Format the given arguments and append the resulting string to an sbuf. */ int sbuf_printf(struct sbuf *s, const char *fmt, ...) { va_list ap; int result; va_start(ap, fmt); result = sbuf_vprintf(s, fmt, ap); va_end(ap); return (result); } /* * Append a character to an sbuf. */ int sbuf_putc(struct sbuf *s, int c) { sbuf_put_byte(s, c); if (s->s_error != 0) return (-1); return (0); } /* * Append a trailing newline to a non-empty sbuf, if one is not already * present. Handles sbufs with drain functions correctly. */ int sbuf_nl_terminate(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); /* * If the s_buf isn't empty, the last byte is simply s_buf[s_len - 1]. * * If the s_buf is empty because a drain function drained it, we * remember if the last byte was a \n with the SBUF_DRAINATEOL flag in * sbuf_drain(). * * In either case, we only append a \n if the previous character was * something else. */ if (s->s_len == 0) { if (!SBUF_ISDRAINATEOL(s)) sbuf_put_byte(s, '\n'); } else if (s->s_buf[s->s_len - 1] != '\n') sbuf_put_byte(s, '\n'); if (s->s_error != 0) return (-1); return (0); } /* * Trim whitespace characters from end of an sbuf. */ int sbuf_trim(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); if (s->s_error != 0) return (-1); while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) { --s->s_len; if (SBUF_ISSECTION(s)) s->s_sect_len--; } return (0); } /* * Check if an sbuf has an error. */ int sbuf_error(const struct sbuf *s) { return (s->s_error); } /* * Finish off an sbuf. */ int sbuf_finish(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); s->s_buf[s->s_len] = '\0'; if (SBUF_NULINCLUDED(s)) s->s_len++; if (s->s_drain_func != NULL) { while (s->s_len > 0 && s->s_error == 0) s->s_error = sbuf_drain(s); } SBUF_SETFLAG(s, SBUF_FINISHED); #ifdef _KERNEL return (s->s_error); #else if (s->s_error != 0) { errno = s->s_error; return (-1); } return (0); #endif } /* * Return a pointer to the sbuf data. */ char * sbuf_data(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, SBUF_FINISHED); KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); return (s->s_buf); } /* * Return the length of the sbuf data. */ ssize_t sbuf_len(struct sbuf *s) { assert_sbuf_integrity(s); /* don't care if it's finished or not */ KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); if (s->s_error != 0) return (-1); /* If finished, nulterm is already in len, else add one. */ if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s)) return (s->s_len + 1); return (s->s_len); } /* * Clear an sbuf, free its buffer if necessary. */ void sbuf_delete(struct sbuf *s) { int isdyn; assert_sbuf_integrity(s); /* don't care if it's finished or not */ if (SBUF_ISDYNAMIC(s)) SBFREE(s->s_buf); isdyn = SBUF_ISDYNSTRUCT(s); memset(s, 0, sizeof(*s)); if (isdyn) SBFREE(s); } /* * Check if an sbuf has been finished. */ int sbuf_done(const struct sbuf *s) { return (SBUF_ISFINISHED(s)); } /* * Start a section. */ void sbuf_start_section(struct sbuf *s, ssize_t *old_lenp) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); if (!SBUF_ISSECTION(s)) { KASSERT(s->s_sect_len == 0, ("s_sect_len != 0 when starting a section")); if (old_lenp != NULL) *old_lenp = -1; s->s_rec_off = s->s_len; SBUF_SETFLAG(s, SBUF_INSECTION); } else { KASSERT(old_lenp != NULL, ("s_sect_len should be saved when starting a subsection")); *old_lenp = s->s_sect_len; s->s_sect_len = 0; } } /* * End the section padding to the specified length with the specified * character. */ ssize_t sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c) { ssize_t len; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(SBUF_ISSECTION(s), ("attempt to end a section when not in a section")); if (pad > 1) { len = roundup(s->s_sect_len, pad) - s->s_sect_len; for (; s->s_error == 0 && len > 0; len--) sbuf_put_byte(s, c); } len = s->s_sect_len; if (old_len == -1) { s->s_rec_off = s->s_sect_len = 0; SBUF_CLEARFLAG(s, SBUF_INSECTION); } else { s->s_sect_len += old_len; } if (s->s_error != 0) return (-1); return (len); } diff --git a/sys/sys/sbuf.h b/sys/sys/sbuf.h index 201c90229732..60dcda53a461 100644 --- a/sys/sys/sbuf.h +++ b/sys/sys/sbuf.h @@ -1,120 +1,120 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000-2008 Poul-Henning Kamp - * Copyright (c) 2000-2008 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2000-2008 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _SYS_SBUF_H_ #define _SYS_SBUF_H_ #include struct sbuf; typedef int (sbuf_drain_func)(void *, const char *, int); /* * Structure definition */ struct sbuf { char *s_buf; /* storage buffer */ sbuf_drain_func *s_drain_func; /* drain function */ void *s_drain_arg; /* user-supplied drain argument */ int s_error; /* current error code */ ssize_t s_size; /* size of storage buffer */ ssize_t s_len; /* current length of string */ #define SBUF_FIXEDLEN 0x00000000 /* fixed length buffer (default) */ #define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */ #define SBUF_INCLUDENUL 0x00000002 /* nulterm byte is counted in len */ #define SBUF_DRAINTOEOR 0x00000004 /* use section 0 as drain EOR marker */ #define SBUF_NOWAIT 0x00000008 /* Extend with non-blocking malloc */ #define SBUF_USRFLAGMSK 0x0000ffff /* mask of flags the user may specify */ #define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */ #define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */ #define SBUF_DYNSTRUCT 0x00080000 /* sbuf must be freed */ #define SBUF_INSECTION 0x00100000 /* set by sbuf_start_section() */ #define SBUF_DRAINATEOL 0x00200000 /* drained contents ended in \n */ int s_flags; /* flags */ ssize_t s_sect_len; /* current length of section */ ssize_t s_rec_off; /* current record start offset */ }; #ifndef HD_COLUMN_MASK #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 #define HD_OMIT_COUNT (1 << 16) #define HD_OMIT_HEX (1 << 17) #define HD_OMIT_CHARS (1 << 18) #endif /* HD_COLUMN_MASK */ __BEGIN_DECLS /* * API functions */ struct sbuf *sbuf_new(struct sbuf *, char *, int, int); #define sbuf_new_auto() \ sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND) int sbuf_get_flags(struct sbuf *); void sbuf_clear_flags(struct sbuf *, int); void sbuf_set_flags(struct sbuf *, int); void sbuf_clear(struct sbuf *); int sbuf_setpos(struct sbuf *, ssize_t); int sbuf_bcat(struct sbuf *, const void *, size_t); int sbuf_bcpy(struct sbuf *, const void *, size_t); int sbuf_cat(struct sbuf *, const char *); int sbuf_cpy(struct sbuf *, const char *); int sbuf_printf(struct sbuf *, const char *, ...) __printflike(2, 3); int sbuf_vprintf(struct sbuf *, const char *, __va_list) __printflike(2, 0); int sbuf_nl_terminate(struct sbuf *); int sbuf_putc(struct sbuf *, int); void sbuf_set_drain(struct sbuf *, sbuf_drain_func *, void *); int sbuf_drain(struct sbuf *); int sbuf_trim(struct sbuf *); int sbuf_error(const struct sbuf *); int sbuf_finish(struct sbuf *); char *sbuf_data(struct sbuf *); ssize_t sbuf_len(struct sbuf *); int sbuf_done(const struct sbuf *); void sbuf_delete(struct sbuf *); void sbuf_start_section(struct sbuf *, ssize_t *); ssize_t sbuf_end_section(struct sbuf *, ssize_t, size_t, int); void sbuf_hexdump(struct sbuf *, const void *, int, const char *, int); int sbuf_count_drain(void *arg, const char *data, int len); int sbuf_printf_drain(void *arg, const char *data, int len); void sbuf_putbuf(struct sbuf *); #ifdef _KERNEL struct uio; struct sbuf *sbuf_uionew(struct sbuf *, struct uio *, int *); int sbuf_bcopyin(struct sbuf *, const void *, size_t); int sbuf_copyin(struct sbuf *, const void *, size_t); #endif __END_DECLS #endif diff --git a/tools/regression/pthread/mutex_isowned_np/mutex_isowned_np.c b/tools/regression/pthread/mutex_isowned_np/mutex_isowned_np.c index c1dd5159dc8a..9032c7dd3968 100644 --- a/tools/regression/pthread/mutex_isowned_np/mutex_isowned_np.c +++ b/tools/regression/pthread/mutex_isowned_np/mutex_isowned_np.c @@ -1,75 +1,75 @@ /*- - * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include static void * thread(void *arg) { pthread_mutex_t *mtx = arg; if (pthread_mutex_isowned_np(mtx) != 0) { printf("pthread_mutex_isowned_np() returned non-zero\n" "for a mutex held by another thread\n"); exit(1); } return (NULL); } int main(void) { pthread_t thr; pthread_mutex_t mtx; pthread_mutex_init(&mtx, NULL); if (pthread_mutex_isowned_np(&mtx) != 0) { printf("pthread_mutex_isowned_np() returned non-zero\n" "for a mutex that is not held\n"); exit(1); } pthread_mutex_lock(&mtx); if (pthread_mutex_isowned_np(&mtx) == 0) { printf("pthread_mutex_isowned_np() returned zero\n" "for a mutex we hold ourselves\n"); exit(1); } pthread_create(&thr, NULL, thread, &mtx); pthread_join(thr, NULL); pthread_mutex_unlock(&mtx); if (pthread_mutex_isowned_np(&mtx) != 0) { printf("pthread_mutex_isowned_np() returned non-zero\n" "for a mutex that is not held\n"); exit(1); } printf("OK\n"); exit(0); } diff --git a/tools/tools/ansify/ansify.pl b/tools/tools/ansify/ansify.pl index 76538b8b0f7e..3664c7fcb484 100644 --- a/tools/tools/ansify/ansify.pl +++ b/tools/tools/ansify/ansify.pl @@ -1,161 +1,161 @@ #!/usr/bin/perl -w #- -# Copyright (c) 2005 Dag-Erling Coïdan Smørgrav +# Copyright (c) 2005 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer # in this position and unchanged. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # use v5.6.0; use strict; use Getopt::Long; my $caddr_t; sub ansify($$$) { my $ifh = shift; my $ofh = shift; my $fn = shift; my $line = 0; OUTER: while (<$ifh>) { # look for K&R-style function definitions if (m/^(\w+)\s*\(([\w,\s]+)\)$/) { my @saved = ($_); my $func = $1; my @args = split(/\s*,\s*/, $2); my $arglist = ""; # capture K&R-style argument list while (<$ifh>) { push(@saved, $_); last if (m/^\{\s*$/); $arglist .= $_; } # remove comments (common in vfs code) $arglist =~ s/\/\*([^*]|\*[^\/])*\*\// /gs; # identify type of each argument my %type; foreach (split('\n', $arglist)) { s/\s+/ /g; if (!/^\s*([\w\s\*]+?)\s*(\w+?);\s*$/) { warn("[$fn:$line] $func(): can't parse argument list\n"); print $ofh @saved; $line += @saved; next OUTER; } $type{$2} = $1; } # construct ANSI-style function definition my $repl = "$func("; foreach my $arg (@args) { # missing arguments in argument list if (!exists($type{$arg})) { warn("[$fn:$line] $func(): unknown type for '$arg' argument\n"); print $ofh @saved; $line += @saved; next OUTER; } if ($caddr_t) { $type{$arg} = "void *" if $type{$arg} eq "caddr_t"; } $repl .= $type{$arg}; $repl .= " " unless ($type{$arg} =~ m/\*$/); $repl .= $arg; $repl .= ", " unless $arg eq $args[-1]; delete $type{$arg}; } $repl .= ")"; # extra arguments in argument list if (%type) { warn("[$fn:$line] $func(): too many arguments\n"); print $ofh @saved; $line += @saved; next OUTER; } print $ofh "$repl\n"; ++$line; # warn about long lines so they can be fixed up manually warn("[$fn:$line] $func(): definition exceeds 80 characters\n") if length($repl) >= 80; print $ofh "{\n"; ++$line; } else { print $ofh $_; ++$line; } } } sub ansify_file($) { my $fn = shift; my $tfn = "$fn.ansify"; local *IN; local *OUT; if (open(IN, "<", $fn)) { if (open(OUT, ">", $tfn)) { ansify(*IN{IO}, *OUT{IO}, $fn); if (!rename($tfn, $fn)) { warn("$fn: $!\n"); unlink($tfn); } } else { warn("$fn.ansify: $!\n"); } } else { warn("$fn: $!\n"); } } sub usage() { print STDERR "usage: ansify [options] [file ...] Options: -c, --caddr_t Replace caddr_t with void * in converted function definitions "; exit(1); } MAIN:{ Getopt::Long::Configure("auto_abbrev", "bundling"); GetOptions( "c|caddr_t" => \$caddr_t, ) or usage(); if (@ARGV) { foreach (@ARGV) { ansify_file($_); } } else { ansify(*STDIN{IO}, *STDOUT{IO}, "(stdin)"); } } diff --git a/tools/tools/genericize/genericize.pl b/tools/tools/genericize/genericize.pl index 5eaf1ab2e830..6ed2d1d60e85 100755 --- a/tools/tools/genericize/genericize.pl +++ b/tools/tools/genericize/genericize.pl @@ -1,136 +1,136 @@ #!/usr/bin/perl -w #- -# Copyright (c) 2004 Dag-Erling Coïdan Smørgrav +# Copyright (c) 2004 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer # in this position and unchanged. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # use strict; use Getopt::Std; sub EMPTY() {} MAIN:{ my %opts; getopts('c', \%opts); my %config; my $machine; my $ident; while (<>) { chomp(); s/\s*(\#.*)?$//; next unless $_; my ($keyword, $values) = split(' ', $_, 2); foreach my $value (split(/,\s*/, $values)) { if ($keyword eq 'machine') { $machine = $value; } elsif ($keyword eq 'ident') { $ident = $value; } elsif ($keyword eq 'options' && $value =~ m/(\w+)=(.+)/) { $config{$keyword}->{$1} = $2; } else { $config{$keyword}->{$value} = \&EMPTY; } } } my $generic; if ($machine) { $generic = "/usr/src/sys/$machine/conf/GENERIC"; } else { ($generic = $ARGV) =~ s|([^/])+$|GENERIC|; } local *GENERIC; open(GENERIC, "<", $generic) or die("$generic: $!\n"); my $blank = 0; while () { my $line = $_; chomp(); if ($opts{'c'} && m/^\s*\#/) { if ($blank) { print "\n"; $blank = 0; } print $line; next; } ++$blank unless $_; s/\s*(\#.*)?$//; next unless $_; my ($keyword, $value) = split(' ', $_); if ($keyword eq 'machine') { die("$generic is for $value, not $machine\n") unless ($value eq $machine); } elsif ($keyword eq 'ident') { $line =~ s/$value/$ident/; } elsif ($keyword eq 'options' && $value =~ m/(\w+)=(.+)/ && $config{$keyword} && $config{$keyword}->{$1} && $config{$keyword}->{$1} != \&EMPTY) { $value = $1; if ($config{$keyword}->{$value} ne $2) { my ($old, $new) = ($2, $config{$keyword}->{$value}); $line =~ s{=$old}{=$new}; } delete($config{$keyword}->{$value}); delete($config{$keyword}) unless %{$config{$keyword}}; } elsif ($config{$keyword} && $config{$keyword}->{$value}) { delete($config{$keyword}->{$value}); delete($config{$keyword}) unless %{$config{$keyword}}; } else { next; } if ($blank) { print "\n"; $blank = 0; } print $line; } close(GENERIC); if (%config) { print "\n# Addenda\n"; foreach my $keyword (sort(keys(%config))) { foreach my $value (sort(keys(%{$config{$keyword}}))) { print "$keyword"; if (length($keyword) < 7) { print "\t"; } elsif (length($keyword) == 7) { print " "; } print "\t$value"; print "=$config{$keyword}->{$value}" unless $config{$keyword}->{$value} == \&EMPTY; print "\n"; } } } exit(0); } diff --git a/tools/tools/hcomp/hcomp.pl b/tools/tools/hcomp/hcomp.pl index 280b5360d200..6d15eef08542 100644 --- a/tools/tools/hcomp/hcomp.pl +++ b/tools/tools/hcomp/hcomp.pl @@ -1,92 +1,92 @@ #!/usr/bin/perl -w #- -# Copyright (c) 2003 Dag-Erling Coïdan Smørgrav +# Copyright (c) 2003 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer # in this position and unchanged. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # use strict; use Getopt::Std; our $opt_b; our $opt_v; sub hcomp($) { my $fn = shift; local *FILE; my $header; warn("$fn\n") if ($opt_v); open(FILE, "<", $fn) or die("$fn: $!\n"); $header = join('', ); close(FILE); # Remove comments $header =~ s|/\*.*?\*/||gs; $header =~ s|//.*$||gm; # Collapse preprocessor directives while ($header =~ s|(\n\#.*?)\\\n|$1|gs) { # nothing } # Remove superfluous whitespace $header =~ s|^\s+||s; $header =~ s|^\s+||gm; $header =~ s|\s+$||s; $header =~ s|\s+$||gm; $header =~ s|\n+|\n|gs; $header =~ s|[ \t]+| |gm; open(FILE, ">", "$fn.new") or die("$fn.new: $!\n"); print(FILE $header); close(FILE); rename($fn, "$fn.$opt_b") if defined($opt_b); rename("$fn.new", $fn); } sub usage() { print(STDERR "usage: hcomp [-b ext] file ...\n"); exit(1); } MAIN:{ my %opts; getopts('b:v') or usage(); foreach (@ARGV) { hcomp($_); } } diff --git a/tools/tools/mtxstat/mtxstat.pl b/tools/tools/mtxstat/mtxstat.pl index c755bbb5288a..eaf95cf1df54 100644 --- a/tools/tools/mtxstat/mtxstat.pl +++ b/tools/tools/mtxstat/mtxstat.pl @@ -1,129 +1,129 @@ #!/usr/bin/perl -Tw #- -# Copyright (c) 2002 Dag-Erling Coïdan Smørgrav +# Copyright (c) 2002 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer # in this position and unchanged. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # use strict; use Getopt::Std; sub usage() { print(STDERR "usage: mtxstat [-gr] [-a|c|m|t] [-l limit]\n"); exit(1); } MAIN:{ my %opts; # Command-line options my $key; # Sort key my $limit; # Output limit local *PIPE; # Pipe my $header; # Header line my @names; # Field names my %data; # Mutex data my @list; # List of entries getopts("acgl:mrt", \%opts) or usage(); if ($opts{'a'}) { usage() if ($opts{'c'} || $opts{'m'} || $opts{'t'}); $key = 'avg'; } elsif ($opts{'c'}) { usage() if ($opts{'m'} || $opts{'t'}); $key = 'count'; } elsif ($opts{'m'}) { usage() if ($opts{'t'}); $key = 'max'; } elsif ($opts{'t'}) { $key = 'total'; } if ($opts{'l'}) { if ($opts{'l'} !~ m/^\d+$/) { usage(); } $limit = $opts{'l'}; } $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin'; open(PIPE, "sysctl -n debug.mutex.prof.stats|") or die("open(): $!\n"); $header = ; chomp($header); @names = split(' ', $header); if (defined($key) && !grep(/^$key$/, @names)) { die("can't find sort key '$key' in header\n"); } while () { chomp(); my @fields = split(' ', $_, @names); next unless @fields; my %entry; foreach (@names) { $entry{$_} = ($_ eq 'name') ? shift(@fields) : 0.0 + shift(@fields); } if ($opts{'g'}) { $entry{'name'} =~ s/^(\S+)\s+\((.*)\)$/$2/; } my $name = $entry{'name'}; if ($data{$name}) { if ($entry{'max'} > $data{$name}->{'max'}) { $data{$name}->{'max'} = $entry{'max'}; } $data{$name}->{'total'} += $entry{'total'}; $data{$name}->{'count'} += $entry{'count'}; $data{$name}->{'avg'} = $data{$name}->{'total'} / $data{$name}->{'count'}; } else { $data{$name} = \%entry; } } if (defined($key)) { @list = sort({ $data{$a}->{$key} <=> $data{$b}->{$key} } sort(keys(%data))); } else { @list = sort(keys(%data)); } if ($opts{'r'}) { @list = reverse(@list); } print("$header\n"); if ($limit) { while (@list > $limit) { pop(@list); } } foreach (@list) { printf("%6.0f %12.0f %11.0f %5.0f %-40.40s\n", $data{$_}->{'max'}, $data{$_}->{'total'}, $data{$_}->{'count'}, $data{$_}->{'avg'}, $data{$_}->{'name'}); } } diff --git a/tools/tools/track/track.sh b/tools/tools/track/track.sh index 9050c9f1b183..31e93166cef2 100644 --- a/tools/tools/track/track.sh +++ b/tools/tools/track/track.sh @@ -1,48 +1,48 @@ #!/bin/sh #- -# Copyright (c) 2008 Dag-Erling Coïdan Smørgrav +# Copyright (c) 2008 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer # in this position and unchanged. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # tail -10000 -F "$@" | while read line do case $line in \>\>\>*) endl="\r\n" ;; \*\*\**) endl="\r\n" ;; \=\=\=*) endl="\r" ;; *) continue ;; esac printf "%s%*s${endl}" "${line}" $((${COLUMNS:-80} - ${#line} - 1)) " " done diff --git a/usr.bin/grep/file.c b/usr.bin/grep/file.c index 2845f0ec225d..cd2764811776 100644 --- a/usr.bin/grep/file.c +++ b/usr.bin/grep/file.c @@ -1,247 +1,247 @@ /* $NetBSD: file.c,v 1.5 2011/02/16 18:35:39 joerg Exp $ */ /* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * Copyright (C) 2008-2010 Gabor Kovesdan * Copyright (C) 2010 Dimitry Andric * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grep.h" #define MAXBUFSIZ (32 * 1024) #define LNBUFBUMP 80 static char *buffer; static char *bufpos; static size_t bufrem; static size_t fsiz; static char *lnbuf; static size_t lnbuflen; static inline int grep_refill(struct file *f) { ssize_t nr; if (filebehave == FILE_MMAP) return (0); bufpos = buffer; bufrem = 0; nr = read(f->fd, buffer, MAXBUFSIZ); if (nr < 0) return (-1); bufrem = nr; return (0); } static inline int grep_lnbufgrow(size_t newlen) { if (lnbuflen < newlen) { lnbuf = grep_realloc(lnbuf, newlen); lnbuflen = newlen; } return (0); } char * grep_fgetln(struct file *f, struct parsec *pc) { char *p; size_t len; size_t off; ptrdiff_t diff; /* Fill the buffer, if necessary */ if (bufrem == 0 && grep_refill(f) != 0) goto error; if (bufrem == 0) { /* Return zero length to indicate EOF */ pc->ln.len= 0; return (bufpos); } /* Look for a newline in the remaining part of the buffer */ if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) { ++p; /* advance over newline */ len = p - bufpos; if (grep_lnbufgrow(len + 1)) goto error; memcpy(lnbuf, bufpos, len); bufrem -= len; bufpos = p; pc->ln.len = len; lnbuf[len] = '\0'; return (lnbuf); } /* We have to copy the current buffered data to the line buffer */ for (len = bufrem, off = 0; ; len += bufrem) { /* Make sure there is room for more data */ if (grep_lnbufgrow(len + LNBUFBUMP)) goto error; memcpy(lnbuf + off, bufpos, len - off); /* With FILE_MMAP, this is EOF; there's no more to refill */ if (filebehave == FILE_MMAP) { bufrem -= len; break; } off = len; /* Fetch more to try and find EOL/EOF */ if (grep_refill(f) != 0) goto error; if (bufrem == 0) /* EOF: return partial line */ break; if ((p = memchr(bufpos, fileeol, bufrem)) == NULL) continue; /* got it: finish up the line (like code above) */ ++p; diff = p - bufpos; len += diff; if (grep_lnbufgrow(len + 1)) goto error; memcpy(lnbuf + off, bufpos, diff); bufrem -= diff; bufpos = p; break; } pc->ln.len = len; lnbuf[len] = '\0'; return (lnbuf); error: pc->ln.len = 0; return (NULL); } /* * Opens a file for processing. */ struct file * grep_open(const char *path) { struct file *f; f = grep_malloc(sizeof *f); memset(f, 0, sizeof *f); if (path == NULL) { /* Processing stdin implies --line-buffered. */ lbflag = true; f->fd = STDIN_FILENO; } else if ((f->fd = open(path, O_RDONLY)) == -1) goto error1; if (filebehave == FILE_MMAP) { struct stat st; if (fstat(f->fd, &st) == -1 || !S_ISREG(st.st_mode)) filebehave = FILE_STDIO; else { int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC; #ifdef MAP_PREFAULT_READ flags |= MAP_PREFAULT_READ; #endif fsiz = st.st_size; buffer = mmap(NULL, fsiz, PROT_READ, flags, f->fd, (off_t)0); if (buffer == MAP_FAILED) filebehave = FILE_STDIO; else { bufrem = st.st_size; bufpos = buffer; madvise(buffer, st.st_size, MADV_SEQUENTIAL); } } } if ((buffer == NULL) || (buffer == MAP_FAILED)) buffer = grep_malloc(MAXBUFSIZ); /* Fill read buffer, also catches errors early */ if (bufrem == 0 && grep_refill(f) != 0) goto error2; /* Check for binary stuff, if necessary */ if (binbehave != BINFILE_TEXT && fileeol != '\0' && memchr(bufpos, '\0', bufrem) != NULL) f->binary = true; return (f); error2: close(f->fd); error1: free(f); return (NULL); } /* * Closes a file. */ void grep_close(struct file *f) { close(f->fd); /* Reset read buffer and line buffer */ if (filebehave == FILE_MMAP) { munmap(buffer, fsiz); buffer = NULL; } bufpos = buffer; bufrem = 0; free(lnbuf); lnbuf = NULL; lnbuflen = 0; } diff --git a/usr.bin/grep/grep.c b/usr.bin/grep/grep.c index cbf52a4df820..9f960f74dbb6 100644 --- a/usr.bin/grep/grep.c +++ b/usr.bin/grep/grep.c @@ -1,724 +1,724 @@ /* $NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $ */ /* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * Copyright (C) 2008-2009 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grep.h" const char *errstr[] = { "", /* 1*/ "(standard input)", /* 2*/ "unknown %s option", /* 3*/ "usage: %s [-abcDEFGHhIiLlmnOopqRSsUVvwxz] [-A num] [-B num] [-C num]\n", /* 4*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", /* 5*/ "\t[--context=num] [--directories=action] [--label] [--line-buffered]\n", /* 6*/ "\t[--null] [pattern] [file ...]\n", /* 7*/ "Binary file %s matches\n", /* 8*/ "%s (BSD grep, GNU compatible) %s\n", }; /* Flags passed to regcomp() and regexec() */ int cflags = REG_NOSUB | REG_NEWLINE; int eflags = REG_STARTEND; bool matchall; /* Searching patterns */ unsigned int patterns; static unsigned int pattern_sz; struct pat *pattern; regex_t *r_pattern; /* Filename exclusion/inclusion patterns */ unsigned int fpatterns, dpatterns; static unsigned int fpattern_sz, dpattern_sz; struct epat *dpattern, *fpattern; /* For regex errors */ char re_error[RE_ERROR_BUF + 1]; /* Command-line flags */ long long Aflag; /* -A x: print x lines trailing each match */ long long Bflag; /* -B x: print x lines leading each match */ bool Hflag; /* -H: always print file name */ bool Lflag; /* -L: only show names of files with no matches */ bool bflag; /* -b: show block numbers for each match */ bool cflag; /* -c: only show a count of matching lines */ bool hflag; /* -h: don't print filename headers */ bool iflag; /* -i: ignore case */ bool lflag; /* -l: only show names of files with matches */ bool mflag; /* -m x: stop reading the files after x matches */ long long mcount; /* count for -m */ long long mlimit; /* requested value for -m */ char fileeol; /* indicator for eol */ bool nflag; /* -n: show line numbers in front of matching lines */ bool oflag; /* -o: print only matching part */ bool qflag; /* -q: quiet mode (don't output anything) */ bool sflag; /* -s: silent mode (ignore errors) */ bool vflag; /* -v: only show non-matching lines */ bool wflag; /* -w: pattern must start and end on word boundaries */ bool xflag; /* -x: pattern must match entire line */ bool lbflag; /* --line-buffered */ bool nullflag; /* --null */ char *label; /* --label */ const char *color; /* --color */ int grepbehave = GREP_BASIC; /* -EFG: type of the regex */ int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ int filebehave = FILE_STDIO; int devbehave = DEV_READ; /* -D: handling of devices */ int dirbehave = DIR_READ; /* -dRr: handling of directories */ int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ bool dexclude, dinclude; /* --exclude-dir and --include-dir */ bool fexclude, finclude; /* --exclude and --include */ enum { BIN_OPT = CHAR_MAX + 1, COLOR_OPT, HELP_OPT, MMAP_OPT, LINEBUF_OPT, LABEL_OPT, NULL_OPT, R_EXCLUDE_OPT, R_INCLUDE_OPT, R_DEXCLUDE_OPT, R_DINCLUDE_OPT }; static inline const char *init_color(const char *); /* Housekeeping */ bool file_err; /* file reading error */ /* * Prints usage information and returns 2. */ static void usage(void) { fprintf(stderr, errstr[3], getprogname()); fprintf(stderr, "%s", errstr[4]); fprintf(stderr, "%s", errstr[5]); fprintf(stderr, "%s", errstr[6]); exit(2); } static const char *optstr = "0123456789A:B:C:D:EFGHILOSRUVabcd:e:f:hilm:nopqrsuvwxyz"; static const struct option long_options[] = { {"binary-files", required_argument, NULL, BIN_OPT}, {"help", no_argument, NULL, HELP_OPT}, {"mmap", no_argument, NULL, MMAP_OPT}, {"line-buffered", no_argument, NULL, LINEBUF_OPT}, {"label", required_argument, NULL, LABEL_OPT}, {"null", no_argument, NULL, NULL_OPT}, {"color", optional_argument, NULL, COLOR_OPT}, {"colour", optional_argument, NULL, COLOR_OPT}, {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, {"include", required_argument, NULL, R_INCLUDE_OPT}, {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, {"after-context", required_argument, NULL, 'A'}, {"text", no_argument, NULL, 'a'}, {"before-context", required_argument, NULL, 'B'}, {"byte-offset", no_argument, NULL, 'b'}, {"context", optional_argument, NULL, 'C'}, {"count", no_argument, NULL, 'c'}, {"devices", required_argument, NULL, 'D'}, {"directories", required_argument, NULL, 'd'}, {"extended-regexp", no_argument, NULL, 'E'}, {"regexp", required_argument, NULL, 'e'}, {"fixed-strings", no_argument, NULL, 'F'}, {"file", required_argument, NULL, 'f'}, {"basic-regexp", no_argument, NULL, 'G'}, {"no-filename", no_argument, NULL, 'h'}, {"with-filename", no_argument, NULL, 'H'}, {"ignore-case", no_argument, NULL, 'i'}, {"files-with-matches", no_argument, NULL, 'l'}, {"files-without-match", no_argument, NULL, 'L'}, {"max-count", required_argument, NULL, 'm'}, {"line-number", no_argument, NULL, 'n'}, {"only-matching", no_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {"silent", no_argument, NULL, 'q'}, {"recursive", no_argument, NULL, 'r'}, {"no-messages", no_argument, NULL, 's'}, {"binary", no_argument, NULL, 'U'}, {"unix-byte-offsets", no_argument, NULL, 'u'}, {"invert-match", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"word-regexp", no_argument, NULL, 'w'}, {"line-regexp", no_argument, NULL, 'x'}, {"null-data", no_argument, NULL, 'z'}, {NULL, no_argument, NULL, 0} }; /* * Adds a searching pattern to the internal array. */ static void add_pattern(char *pat, size_t len) { /* Check if we can do a shortcut */ if (len == 0) { matchall = true; return; } /* Increase size if necessary */ if (patterns == pattern_sz) { pattern_sz *= 2; pattern = grep_realloc(pattern, ++pattern_sz * sizeof(struct pat)); } if (len > 0 && pat[len - 1] == '\n') --len; /* pat may not be NUL-terminated */ pattern[patterns].pat = grep_malloc(len + 1); memcpy(pattern[patterns].pat, pat, len); pattern[patterns].len = len; pattern[patterns].pat[len] = '\0'; ++patterns; } /* * Adds a file include/exclude pattern to the internal array. */ static void add_fpattern(const char *pat, int mode) { /* Increase size if necessary */ if (fpatterns == fpattern_sz) { fpattern_sz *= 2; fpattern = grep_realloc(fpattern, ++fpattern_sz * sizeof(struct epat)); } fpattern[fpatterns].pat = grep_strdup(pat); fpattern[fpatterns].mode = mode; ++fpatterns; } /* * Adds a directory include/exclude pattern to the internal array. */ static void add_dpattern(const char *pat, int mode) { /* Increase size if necessary */ if (dpatterns == dpattern_sz) { dpattern_sz *= 2; dpattern = grep_realloc(dpattern, ++dpattern_sz * sizeof(struct epat)); } dpattern[dpatterns].pat = grep_strdup(pat); dpattern[dpatterns].mode = mode; ++dpatterns; } /* * Reads searching patterns from a file and adds them with add_pattern(). */ static void read_patterns(const char *fn) { struct stat st; FILE *f; char *line; size_t len; ssize_t rlen; if (strcmp(fn, "-") == 0) f = stdin; else if ((f = fopen(fn, "r")) == NULL) err(2, "%s", fn); if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { fclose(f); return; } len = 0; line = NULL; while ((rlen = getline(&line, &len, f)) != -1) { if (line[0] == '\0') continue; add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen); } free(line); if (ferror(f)) err(2, "%s", fn); if (strcmp(fn, "-") != 0) fclose(f); } static inline const char * init_color(const char *d) { char *c; c = getenv("GREP_COLOR"); return (c != NULL && c[0] != '\0' ? c : d); } int main(int argc, char *argv[]) { char **aargv, **eargv, *eopts; char *ep; const char *pn; long long l; unsigned int aargc, eargc, i; int c, lastc, needpattern, newarg, prevoptind; bool matched; setlocale(LC_ALL, ""); /* * Check how we've bene invoked to determine the behavior we should * exhibit. In this way we can have all the functionalities in one * binary without the need of scripting and using ugly hacks. */ pn = getprogname(); switch (pn[0]) { case 'e': grepbehave = GREP_EXTENDED; break; case 'f': grepbehave = GREP_FIXED; break; case 'r': dirbehave = DIR_RECURSE; Hflag = true; break; } lastc = '\0'; newarg = 1; prevoptind = 1; needpattern = 1; fileeol = '\n'; eopts = getenv("GREP_OPTIONS"); /* support for extra arguments in GREP_OPTIONS */ eargc = 0; if (eopts != NULL && eopts[0] != '\0') { char *str; /* make an estimation of how many extra arguments we have */ for (unsigned int j = 0; j < strlen(eopts); j++) if (eopts[j] == ' ') eargc++; eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); eargc = 0; /* parse extra arguments */ while ((str = strsep(&eopts, " ")) != NULL) if (str[0] != '\0') eargv[eargc++] = grep_strdup(str); aargv = (char **)grep_calloc(eargc + argc + 1, sizeof(char *)); aargv[0] = argv[0]; for (i = 0; i < eargc; i++) aargv[i + 1] = eargv[i]; for (int j = 1; j < argc; j++, i++) aargv[i + 1] = argv[j]; aargc = eargc + argc; } else { aargv = argv; aargc = argc; } while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != -1)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (newarg || !isdigit(lastc)) Aflag = 0; else if (Aflag > LLONG_MAX / 10 - 1) { errno = ERANGE; err(2, NULL); } Aflag = Bflag = (Aflag * 10) + (c - '0'); break; case 'C': if (optarg == NULL) { Aflag = Bflag = 2; break; } /* FALLTHROUGH */ case 'A': /* FALLTHROUGH */ case 'B': errno = 0; l = strtoll(optarg, &ep, 10); if (errno == ERANGE || errno == EINVAL) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } else if (l < 0) { errno = EINVAL; err(2, "context argument must be non-negative"); } if (c == 'A') Aflag = l; else if (c == 'B') Bflag = l; else Aflag = Bflag = l; break; case 'a': binbehave = BINFILE_TEXT; break; case 'b': bflag = true; break; case 'c': cflag = true; break; case 'D': if (strcasecmp(optarg, "skip") == 0) devbehave = DEV_SKIP; else if (strcasecmp(optarg, "read") == 0) devbehave = DEV_READ; else errx(2, errstr[2], "--devices"); break; case 'd': if (strcasecmp("recurse", optarg) == 0) { Hflag = true; dirbehave = DIR_RECURSE; } else if (strcasecmp("skip", optarg) == 0) dirbehave = DIR_SKIP; else if (strcasecmp("read", optarg) == 0) dirbehave = DIR_READ; else errx(2, errstr[2], "--directories"); break; case 'E': grepbehave = GREP_EXTENDED; break; case 'e': { char *token; char *string = optarg; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); } needpattern = 0; break; case 'F': grepbehave = GREP_FIXED; break; case 'f': read_patterns(optarg); needpattern = 0; break; case 'G': grepbehave = GREP_BASIC; break; case 'H': Hflag = true; break; case 'h': Hflag = false; hflag = true; break; case 'I': binbehave = BINFILE_SKIP; break; case 'i': case 'y': iflag = true; cflags |= REG_ICASE; break; case 'L': lflag = false; Lflag = true; break; case 'l': Lflag = false; lflag = true; break; case 'm': mflag = true; errno = 0; mlimit = mcount = strtoll(optarg, &ep, 10); if (((errno == ERANGE) && (mcount == LLONG_MAX)) || ((errno == EINVAL) && (mcount == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } break; case 'n': nflag = true; break; case 'O': linkbehave = LINK_EXPLICIT; break; case 'o': oflag = true; cflags &= ~REG_NOSUB; break; case 'p': linkbehave = LINK_SKIP; break; case 'q': qflag = true; break; case 'S': linkbehave = LINK_READ; break; case 'R': case 'r': dirbehave = DIR_RECURSE; Hflag = true; break; case 's': sflag = true; break; case 'U': binbehave = BINFILE_BIN; break; case 'u': case MMAP_OPT: filebehave = FILE_MMAP; break; case 'V': printf(errstr[8], getprogname(), VERSION); exit(0); case 'v': vflag = true; break; case 'w': wflag = true; cflags &= ~REG_NOSUB; break; case 'x': xflag = true; cflags &= ~REG_NOSUB; break; case 'z': fileeol = '\0'; cflags &= ~REG_NEWLINE; break; case BIN_OPT: if (strcasecmp("binary", optarg) == 0) binbehave = BINFILE_BIN; else if (strcasecmp("without-match", optarg) == 0) binbehave = BINFILE_SKIP; else if (strcasecmp("text", optarg) == 0) binbehave = BINFILE_TEXT; else errx(2, errstr[2], "--binary-files"); break; case COLOR_OPT: color = NULL; if (optarg == NULL || strcasecmp("auto", optarg) == 0 || strcasecmp("tty", optarg) == 0 || strcasecmp("if-tty", optarg) == 0) { char *term; term = getenv("TERM"); if (isatty(STDOUT_FILENO) && term != NULL && strcasecmp(term, "dumb") != 0) color = init_color("01;31"); } else if (strcasecmp("always", optarg) == 0 || strcasecmp("yes", optarg) == 0 || strcasecmp("force", optarg) == 0) { color = init_color("01;31"); } else if (strcasecmp("never", optarg) != 0 && strcasecmp("none", optarg) != 0 && strcasecmp("no", optarg) != 0) errx(2, errstr[2], "--color"); cflags &= ~REG_NOSUB; break; case LABEL_OPT: label = optarg; break; case LINEBUF_OPT: lbflag = true; break; case NULL_OPT: nullflag = true; break; case R_INCLUDE_OPT: finclude = true; add_fpattern(optarg, INCL_PAT); break; case R_EXCLUDE_OPT: fexclude = true; add_fpattern(optarg, EXCL_PAT); break; case R_DINCLUDE_OPT: dinclude = true; add_dpattern(optarg, INCL_PAT); break; case R_DEXCLUDE_OPT: dexclude = true; add_dpattern(optarg, EXCL_PAT); break; case HELP_OPT: default: usage(); } lastc = c; newarg = optind != prevoptind; prevoptind = optind; } aargc -= optind; aargv += optind; /* xflag takes precedence, don't confuse the matching bits. */ if (wflag && xflag) wflag = false; /* Fail if we don't have any pattern */ if (aargc == 0 && needpattern) usage(); /* Process patterns from command line */ if (aargc != 0 && needpattern) { char *token; char *string = *aargv; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); --aargc; ++aargv; } switch (grepbehave) { case GREP_BASIC: break; case GREP_FIXED: /* * regex(3) implementations that support fixed-string searches generally * define either REG_NOSPEC or REG_LITERAL. Set the appropriate flag * here. If neither are defined, GREP_FIXED later implies that the * internal literal matcher should be used. Other cflags that have * the same interpretation as REG_NOSPEC and REG_LITERAL should be * similarly added here, and grep.h should be amended to take this into * consideration when defining WITH_INTERNAL_NOSPEC. */ #if defined(REG_NOSPEC) cflags |= REG_NOSPEC; #elif defined(REG_LITERAL) cflags |= REG_LITERAL; #endif break; case GREP_EXTENDED: cflags |= REG_EXTENDED; break; default: /* NOTREACHED */ usage(); } r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); #ifdef WITH_INTERNAL_NOSPEC if (grepbehave != GREP_FIXED) { #else { #endif /* Check if cheating is allowed (always is for fgrep). */ for (i = 0; i < patterns; ++i) { c = regcomp(&r_pattern[i], pattern[i].pat, cflags); if (c != 0) { regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF); errx(2, "%s", re_error); } } } if (lbflag) setlinebuf(stdout); if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true; initqueue(); if (aargc == 0 && dirbehave != DIR_RECURSE) exit(!procfile("-")); if (dirbehave == DIR_RECURSE) matched = grep_tree(aargv); else for (matched = false; aargc--; ++aargv) { if ((finclude || fexclude) && !file_matching(*aargv)) continue; if (procfile(*aargv)) matched = true; } if (Lflag) matched = !matched; /* * Calculate the correct return value according to the * results and the command line option. */ exit(matched ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); } diff --git a/usr.bin/grep/grep.h b/usr.bin/grep/grep.h index 02f612aecf1f..211fe1ae75c8 100644 --- a/usr.bin/grep/grep.h +++ b/usr.bin/grep/grep.h @@ -1,159 +1,159 @@ /* $NetBSD: grep.h,v 1.5 2011/02/27 17:33:37 joerg Exp $ */ /* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * Copyright (c) 2008-2009 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include extern const char *errstr[]; #define VERSION "2.6.0-FreeBSD" #define GREP_FIXED 0 #define GREP_BASIC 1 #define GREP_EXTENDED 2 #if !defined(REG_NOSPEC) && !defined(REG_LITERAL) #define WITH_INTERNAL_NOSPEC #endif #define BINFILE_BIN 0 #define BINFILE_SKIP 1 #define BINFILE_TEXT 2 #define FILE_STDIO 0 #define FILE_MMAP 1 #define DIR_READ 0 #define DIR_SKIP 1 #define DIR_RECURSE 2 #define DEV_READ 0 #define DEV_SKIP 1 #define LINK_READ 0 #define LINK_EXPLICIT 1 #define LINK_SKIP 2 #define EXCL_PAT 0 #define INCL_PAT 1 #define MAX_MATCHES 32 struct file { int fd; bool binary; }; struct str { off_t boff; off_t off; size_t len; char *dat; char *file; int line_no; }; struct pat { char *pat; int len; }; struct epat { char *pat; int mode; }; /* * Parsing context; used to hold things like matches made and * other useful bits */ struct parsec { regmatch_t matches[MAX_MATCHES]; /* Matches made */ /* XXX TODO: This should be a chunk, not a line */ struct str ln; /* Current line */ size_t lnstart; /* Position in line */ size_t matchidx; /* Latest match index */ int printed; /* Metadata printed? */ bool binary; /* Binary file? */ bool cntlines; /* Count lines? */ }; /* Flags passed to regcomp() and regexec() */ extern int cflags, eflags; /* Command line flags */ extern bool Eflag, Fflag, Gflag, Hflag, Lflag, bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, qflag, sflag, vflag, wflag, xflag; extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag; extern long long Aflag, Bflag; extern long long mcount; extern long long mlimit; extern char fileeol; extern char *label; extern const char *color; extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave; extern bool file_err, matchall; extern unsigned int dpatterns, fpatterns, patterns; extern struct pat *pattern; extern struct epat *dpattern, *fpattern; extern regex_t *er_pattern, *r_pattern; /* For regex errors */ #define RE_ERROR_BUF 512 extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */ /* util.c */ bool file_matching(const char *fname); bool procfile(const char *fn); bool grep_tree(char **argv); void *grep_malloc(size_t size); void *grep_calloc(size_t nmemb, size_t size); void *grep_realloc(void *ptr, size_t size); char *grep_strdup(const char *str); void grep_printline(struct str *line, int sep); /* queue.c */ void initqueue(void); bool enqueue(struct str *x); void printqueue(void); void clearqueue(void); /* file.c */ void grep_close(struct file *f); struct file *grep_open(const char *path); char *grep_fgetln(struct file *f, struct parsec *pc); diff --git a/usr.bin/grep/queue.c b/usr.bin/grep/queue.c index 3ef8a640b37d..6c4815992d0f 100644 --- a/usr.bin/grep/queue.c +++ b/usr.bin/grep/queue.c @@ -1,143 +1,143 @@ /* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * All rights reserved. * Copyright (c) 2020 Kyle Evans * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * A really poor man's queue. It does only what it has to and gets out of * Dodge. It is used in place of to get a better performance. */ #include #include #include #include #include #include "grep.h" typedef struct str qentry_t; static long long filled; static qentry_t *qend, *qpool; /* * qnext is the next entry to populate. qlist is where the list actually * starts, for the purposes of printing. */ static qentry_t *qlist, *qnext; void initqueue(void) { qlist = qnext = qpool = grep_calloc(Bflag, sizeof(*qpool)); qend = qpool + (Bflag - 1); } static qentry_t * advqueue(qentry_t *itemp) { if (itemp == qend) return (qpool); return (itemp + 1); } /* * Enqueue another line; return true if we've dequeued a line as a result */ bool enqueue(struct str *x) { qentry_t *item; bool rotated; item = qnext; qnext = advqueue(qnext); rotated = false; if (filled < Bflag) { filled++; } else if (filled == Bflag) { /* We had already filled up coming in; just rotate. */ qlist = advqueue(qlist); rotated = true; free(item->dat); } /* len + 1 for NUL-terminator */ item->dat = grep_malloc(sizeof(char) * x->len + 1); item->len = x->len; item->line_no = x->line_no; item->boff = x->boff; item->off = x->off; memcpy(item->dat, x->dat, x->len); item->dat[x->len] = '\0'; item->file = x->file; return (rotated); } void printqueue(void) { qentry_t *item; item = qlist; do { /* Buffer must have ended early. */ if (item->dat == NULL) break; grep_printline(item, '-'); free(item->dat); item->dat = NULL; item = advqueue(item); } while (item != qlist); qlist = qnext = qpool; filled = 0; } void clearqueue(void) { qentry_t *item; item = qlist; do { free(item->dat); item->dat = NULL; item = advqueue(item); } while (item != qlist); qlist = qnext = qpool; filled = 0; } diff --git a/usr.bin/grep/util.c b/usr.bin/grep/util.c index 68bfd504279e..1805cc74bfc6 100644 --- a/usr.bin/grep/util.c +++ b/usr.bin/grep/util.c @@ -1,792 +1,792 @@ /* $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $ */ /* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * Copyright (C) 2008-2010 Gabor Kovesdan * Copyright (C) 2017 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grep.h" static bool first_match = true; /* * Match printing context */ struct mprintc { long long tail; /* Number of trailing lines to record */ int last_outed; /* Number of lines since last output */ bool doctx; /* Printing context? */ bool printmatch; /* Printing matches? */ bool same_file; /* Same file as previously printed? */ }; static void procmatch_match(struct mprintc *mc, struct parsec *pc); static void procmatch_nomatch(struct mprintc *mc, struct parsec *pc); static bool procmatches(struct mprintc *mc, struct parsec *pc, bool matched); #ifdef WITH_INTERNAL_NOSPEC static int litexec(const struct pat *pat, const char *string, size_t nmatch, regmatch_t pmatch[]); #endif static bool procline(struct parsec *pc); static void printline(struct parsec *pc, int sep); static void printline_metadata(struct str *line, int sep); bool file_matching(const char *fname) { char *fname_base, *fname_buf; bool ret; ret = finclude ? false : true; fname_buf = strdup(fname); if (fname_buf == NULL) err(2, "strdup"); fname_base = basename(fname_buf); for (unsigned int i = 0; i < fpatterns; ++i) { if (fnmatch(fpattern[i].pat, fname, 0) == 0 || fnmatch(fpattern[i].pat, fname_base, 0) == 0) /* * The last pattern matched wins exclusion/inclusion * rights, so we can't reasonably bail out early here. */ ret = (fpattern[i].mode != EXCL_PAT); } free(fname_buf); return (ret); } static inline bool dir_matching(const char *dname) { bool ret; ret = dinclude ? false : true; for (unsigned int i = 0; i < dpatterns; ++i) { if (dname != NULL && fnmatch(dpattern[i].pat, dname, 0) == 0) /* * The last pattern matched wins exclusion/inclusion * rights, so we can't reasonably bail out early here. */ ret = (dpattern[i].mode != EXCL_PAT); } return (ret); } /* * Processes a directory when a recursive search is performed with * the -R option. Each appropriate file is passed to procfile(). */ bool grep_tree(char **argv) { FTS *fts; FTSENT *p; int fts_flags; bool matched, ok; const char *wd[] = { ".", NULL }; matched = false; /* This switch effectively initializes 'fts_flags' */ switch(linkbehave) { case LINK_EXPLICIT: fts_flags = FTS_COMFOLLOW; break; case LINK_SKIP: fts_flags = FTS_PHYSICAL; break; default: fts_flags = FTS_LOGICAL; } fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; fts = fts_open((argv[0] == NULL) ? __DECONST(char * const *, wd) : argv, fts_flags, NULL); if (fts == NULL) err(2, "fts_open"); while (errno = 0, (p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DNR: /* FALLTHROUGH */ case FTS_ERR: file_err = true; if(!sflag) warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); break; case FTS_D: /* FALLTHROUGH */ case FTS_DP: if (dexclude || dinclude) if (!dir_matching(p->fts_name) || !dir_matching(p->fts_path)) fts_set(fts, p, FTS_SKIP); break; case FTS_DC: /* Print a warning for recursive directory loop */ warnx("warning: %s: recursive directory loop", p->fts_path); break; default: /* Check for file exclusion/inclusion */ ok = true; if (fexclude || finclude) ok &= file_matching(p->fts_path); if (ok && procfile(p->fts_path)) matched = true; break; } } if (errno != 0) err(2, "fts_read"); fts_close(fts); return (matched); } static void procmatch_match(struct mprintc *mc, struct parsec *pc) { if (mc->doctx) { if (!first_match && (!mc->same_file || mc->last_outed > 0)) printf("--\n"); if (Bflag > 0) printqueue(); mc->tail = Aflag; } /* Print the matching line, but only if not quiet/binary */ if (mc->printmatch) { printline(pc, ':'); while (pc->matchidx >= MAX_MATCHES) { /* Reset matchidx and try again */ pc->matchidx = 0; if (procline(pc) == !vflag) printline(pc, ':'); else break; } first_match = false; mc->same_file = true; mc->last_outed = 0; } } static void procmatch_nomatch(struct mprintc *mc, struct parsec *pc) { /* Deal with any -A context as needed */ if (mc->tail > 0) { grep_printline(&pc->ln, '-'); mc->tail--; if (Bflag > 0) clearqueue(); } else if (Bflag == 0 || (Bflag > 0 && enqueue(&pc->ln))) /* * Enqueue non-matching lines for -B context. If we're not * actually doing -B context or if the enqueue resulted in a * line being rotated out, then go ahead and increment * last_outed to signify a gap between context/match. */ ++mc->last_outed; } /* * Process any matches in the current parsing context, return a boolean * indicating whether we should halt any further processing or not. 'true' to * continue processing, 'false' to halt. */ static bool procmatches(struct mprintc *mc, struct parsec *pc, bool matched) { if (mflag && mcount <= 0) { /* * We already hit our match count, but we need to keep dumping * lines until we've lost our tail. */ grep_printline(&pc->ln, '-'); mc->tail--; return (mc->tail != 0); } /* * XXX TODO: This should loop over pc->matches and handle things on a * line-by-line basis, setting up a `struct str` as needed. */ /* Deal with any -B context or context separators */ if (matched) { procmatch_match(mc, pc); /* Count the matches if we have a match limit */ if (mflag) { /* XXX TODO: Decrement by number of matched lines */ mcount -= 1; if (mcount <= 0) return (mc->tail != 0); } } else if (mc->doctx) procmatch_nomatch(mc, pc); return (true); } /* * Opens a file and processes it. Each file is processed line-by-line * passing the lines to procline(). */ bool procfile(const char *fn) { struct parsec pc; struct mprintc mc; struct file *f; struct stat sb; mode_t s; int lines; bool line_matched; if (strcmp(fn, "-") == 0) { fn = label != NULL ? label : errstr[1]; f = grep_open(NULL); } else { if (stat(fn, &sb) == 0) { /* Check if we need to process the file */ s = sb.st_mode & S_IFMT; if (dirbehave == DIR_SKIP && s == S_IFDIR) return (false); if (devbehave == DEV_SKIP && (s == S_IFIFO || s == S_IFCHR || s == S_IFBLK || s == S_IFSOCK)) return (false); } f = grep_open(fn); } if (f == NULL) { file_err = true; if (!sflag) warn("%s", fn); return (false); } pc.ln.file = grep_strdup(fn); pc.ln.line_no = 0; pc.ln.len = 0; pc.ln.boff = 0; pc.ln.off = -1; pc.binary = f->binary; pc.cntlines = false; memset(&mc, 0, sizeof(mc)); mc.printmatch = true; if ((pc.binary && binbehave == BINFILE_BIN) || cflag || qflag || lflag || Lflag) mc.printmatch = false; if (mc.printmatch && (Aflag != 0 || Bflag != 0)) mc.doctx = true; if (mc.printmatch && (Aflag != 0 || Bflag != 0 || mflag || nflag)) pc.cntlines = true; mcount = mlimit; for (lines = 0; lines == 0 || !(lflag || qflag); ) { /* * XXX TODO: We need to revisit this in a chunking world. We're * not going to be doing per-line statistics because of the * overhead involved. procmatches can figure that stuff out as * needed. */ /* Reset per-line statistics */ pc.printed = 0; pc.matchidx = 0; pc.lnstart = 0; pc.ln.boff = 0; pc.ln.off += pc.ln.len + 1; /* XXX TODO: Grab a chunk */ if ((pc.ln.dat = grep_fgetln(f, &pc)) == NULL || pc.ln.len == 0) break; if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol) --pc.ln.len; pc.ln.line_no++; /* Return if we need to skip a binary file */ if (pc.binary && binbehave == BINFILE_SKIP) { grep_close(f); free(pc.ln.file); free(f); return (0); } if (mflag && mcount <= 0) { /* * Short-circuit, already hit match count and now we're * just picking up any remaining pieces. */ if (!procmatches(&mc, &pc, false)) break; continue; } line_matched = procline(&pc) == !vflag; if (line_matched) ++lines; /* Halt processing if we hit our match limit */ if (!procmatches(&mc, &pc, line_matched)) break; } if (Bflag > 0) clearqueue(); grep_close(f); if (cflag && !qflag) { if (!hflag) printf("%s:", pc.ln.file); printf("%u\n", lines); } if (lflag && !qflag && lines != 0) printf("%s%c", fn, nullflag ? 0 : '\n'); if (Lflag && !qflag && lines == 0) printf("%s%c", fn, nullflag ? 0 : '\n'); if (lines != 0 && !cflag && !lflag && !Lflag && binbehave == BINFILE_BIN && f->binary && !qflag) printf(errstr[7], fn); free(pc.ln.file); free(f); return (lines != 0); } #ifdef WITH_INTERNAL_NOSPEC /* * Internal implementation of literal string search within a string, modeled * after regexec(3), for use when the regex(3) implementation doesn't offer * either REG_NOSPEC or REG_LITERAL. This does not apply in the default FreeBSD * config, but in other scenarios such as building against libgnuregex or on * some non-FreeBSD OSes. */ static int litexec(const struct pat *pat, const char *string, size_t nmatch, regmatch_t pmatch[]) { char *(*strstr_fn)(const char *, const char *); char *sub, *subject; const char *search; size_t idx, n, ofs, stringlen; if (cflags & REG_ICASE) strstr_fn = strcasestr; else strstr_fn = strstr; idx = 0; ofs = pmatch[0].rm_so; stringlen = pmatch[0].rm_eo; if (ofs >= stringlen) return (REG_NOMATCH); subject = strndup(string, stringlen); if (subject == NULL) return (REG_ESPACE); for (n = 0; ofs < stringlen;) { search = (subject + ofs); if ((unsigned long)pat->len > strlen(search)) break; sub = strstr_fn(search, pat->pat); /* * Ignoring the empty string possibility due to context: grep optimizes * for empty patterns and will never reach this point. */ if (sub == NULL) break; ++n; /* Fill in pmatch if necessary */ if (nmatch > 0) { pmatch[idx].rm_so = ofs + (sub - search); pmatch[idx].rm_eo = pmatch[idx].rm_so + pat->len; if (++idx == nmatch) break; ofs = pmatch[idx].rm_so + 1; } else /* We only needed to know if we match or not */ break; } free(subject); if (n > 0 && nmatch > 0) for (n = idx; n < nmatch; ++n) pmatch[n].rm_so = pmatch[n].rm_eo = -1; return (n > 0 ? 0 : REG_NOMATCH); } #endif /* WITH_INTERNAL_NOSPEC */ #define iswword(x) (iswalnum((x)) || (x) == L'_') /* * Processes a line comparing it with the specified patterns. Each pattern * is looped to be compared along with the full string, saving each and every * match, which is necessary to colorize the output and to count the * matches. The matching lines are passed to printline() to display the * appropriate output. */ static bool procline(struct parsec *pc) { regmatch_t pmatch, lastmatch, chkmatch; wchar_t wbegin, wend; size_t st, nst; unsigned int i; int r = 0, leflags = eflags; size_t startm = 0, matchidx; unsigned int retry; bool lastmatched, matched; matchidx = pc->matchidx; /* Null pattern shortcuts. */ if (matchall) { if (xflag && pc->ln.len == 0) { /* Matches empty lines (-x). */ return (true); } else if (!wflag && !xflag) { /* Matches every line (no -w or -x). */ return (true); } /* * If we only have the NULL pattern, whether we match or not * depends on if we got here with -w or -x. If either is set, * the answer is no. If we have other patterns, we'll defer * to them. */ if (patterns == 0) { return (!(wflag || xflag)); } } else if (patterns == 0) { /* Pattern file with no patterns. */ return (false); } matched = false; st = pc->lnstart; nst = 0; /* Initialize to avoid a false positive warning from GCC. */ lastmatch.rm_so = lastmatch.rm_eo = 0; /* Loop to process the whole line */ while (st <= pc->ln.len) { lastmatched = false; startm = matchidx; retry = 0; if (st > 0 && pc->ln.dat[st - 1] != fileeol) leflags |= REG_NOTBOL; /* Loop to compare with all the patterns */ for (i = 0; i < patterns; i++) { pmatch.rm_so = st; pmatch.rm_eo = pc->ln.len; #ifdef WITH_INTERNAL_NOSPEC if (grepbehave == GREP_FIXED) r = litexec(&pattern[i], pc->ln.dat, 1, &pmatch); else #endif r = regexec(&r_pattern[i], pc->ln.dat, 1, &pmatch, leflags); if (r != 0) continue; /* Check for full match */ if (xflag && (pmatch.rm_so != 0 || (size_t)pmatch.rm_eo != pc->ln.len)) continue; /* Check for whole word match */ if (wflag) { wbegin = wend = L' '; if (pmatch.rm_so != 0 && sscanf(&pc->ln.dat[pmatch.rm_so - 1], "%lc", &wbegin) != 1) r = REG_NOMATCH; else if ((size_t)pmatch.rm_eo != pc->ln.len && sscanf(&pc->ln.dat[pmatch.rm_eo], "%lc", &wend) != 1) r = REG_NOMATCH; else if (iswword(wbegin) || iswword(wend)) r = REG_NOMATCH; /* * If we're doing whole word matching and we * matched once, then we should try the pattern * again after advancing just past the start of * the earliest match. This allows the pattern * to match later on in the line and possibly * still match a whole word. */ if (r == REG_NOMATCH && (retry == pc->lnstart || (unsigned int)pmatch.rm_so + 1 < retry)) retry = pmatch.rm_so + 1; if (r == REG_NOMATCH) continue; } lastmatched = true; lastmatch = pmatch; if (matchidx == 0) matched = true; /* * Replace previous match if the new one is earlier * and/or longer. This will lead to some amount of * extra work if -o/--color are specified, but it's * worth it from a correctness point of view. */ if (matchidx > startm) { chkmatch = pc->matches[matchidx - 1]; if (pmatch.rm_so < chkmatch.rm_so || (pmatch.rm_so == chkmatch.rm_so && (pmatch.rm_eo - pmatch.rm_so) > (chkmatch.rm_eo - chkmatch.rm_so))) { pc->matches[matchidx - 1] = pmatch; nst = pmatch.rm_eo; } } else { /* Advance as normal if not */ pc->matches[matchidx++] = pmatch; nst = pmatch.rm_eo; } /* avoid excessive matching - skip further patterns */ if ((color == NULL && !oflag) || qflag || lflag || matchidx >= MAX_MATCHES) { pc->lnstart = nst; lastmatched = false; break; } } /* * Advance to just past the start of the earliest match, try * again just in case we still have a chance to match later in * the string. */ if (!lastmatched && retry > pc->lnstart) { st = retry; continue; } /* XXX TODO: We will need to keep going, since we're chunky */ /* One pass if we are not recording matches */ if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag)) break; /* If we didn't have any matches or REG_NOSUB set */ if (!lastmatched || (cflags & REG_NOSUB)) nst = pc->ln.len; if (!lastmatched) /* No matches */ break; else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo) /* Zero-length match -- advance one more so we don't get stuck */ nst++; /* Advance st based on previous matches */ st = nst; pc->lnstart = st; } /* Reflect the new matchidx in the context */ pc->matchidx = matchidx; return matched; } /* * Safe malloc() for internal use. */ void * grep_malloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) err(2, "malloc"); return (ptr); } /* * Safe calloc() for internal use. */ void * grep_calloc(size_t nmemb, size_t size) { void *ptr; if ((ptr = calloc(nmemb, size)) == NULL) err(2, "calloc"); return (ptr); } /* * Safe realloc() for internal use. */ void * grep_realloc(void *ptr, size_t size) { if ((ptr = realloc(ptr, size)) == NULL) err(2, "realloc"); return (ptr); } /* * Safe strdup() for internal use. */ char * grep_strdup(const char *str) { char *ret; if ((ret = strdup(str)) == NULL) err(2, "strdup"); return (ret); } /* * Print an entire line as-is, there are no inline matches to consider. This is * used for printing context. */ void grep_printline(struct str *line, int sep) { printline_metadata(line, sep); fwrite(line->dat, line->len, 1, stdout); putchar(fileeol); } static void printline_metadata(struct str *line, int sep) { bool printsep; printsep = false; if (!hflag) { if (!nullflag) { fputs(line->file, stdout); printsep = true; } else { printf("%s", line->file); putchar(0); } } if (nflag) { if (printsep) putchar(sep); printf("%d", line->line_no); printsep = true; } if (bflag) { if (printsep) putchar(sep); printf("%lld", (long long)(line->off + line->boff)); printsep = true; } if (printsep) putchar(sep); } /* * Prints a matching line according to the command line options. */ static void printline(struct parsec *pc, int sep) { size_t a = 0; size_t i, matchidx; regmatch_t match; /* If matchall, everything matches but don't actually print for -o */ if (oflag && matchall) return; matchidx = pc->matchidx; /* --color and -o */ if ((oflag || color) && matchidx > 0) { /* Only print metadata once per line if --color */ if (!oflag && pc->printed == 0) printline_metadata(&pc->ln, sep); for (i = 0; i < matchidx; i++) { match = pc->matches[i]; /* Don't output zero length matches */ if (match.rm_so == match.rm_eo) continue; /* * Metadata is printed on a per-line basis, so every * match gets file metadata with the -o flag. */ if (oflag) { pc->ln.boff = match.rm_so; printline_metadata(&pc->ln, sep); } else fwrite(pc->ln.dat + a, match.rm_so - a, 1, stdout); if (color) fprintf(stdout, "\33[%sm\33[K", color); fwrite(pc->ln.dat + match.rm_so, match.rm_eo - match.rm_so, 1, stdout); if (color) fprintf(stdout, "\33[m\33[K"); a = match.rm_eo; if (oflag) putchar('\n'); } if (!oflag) { if (pc->ln.len - a > 0) fwrite(pc->ln.dat + a, pc->ln.len - a, 1, stdout); putchar('\n'); } } else grep_printline(&pc->ln, sep); pc->printed++; } diff --git a/usr.bin/logins/logins.1 b/usr.bin/logins/logins.1 index bdd00ed96a03..2b6f27d71f1d 100644 --- a/usr.bin/logins/logins.1 +++ b/usr.bin/logins/logins.1 @@ -1,102 +1,102 @@ .\"- -.\" Copyright (c) 2004 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2004 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd March 6, 2004 .Dt LOGINS 1 .Os .Sh NAME .Nm logins .Nd display account information .Sh SYNOPSIS .Nm .Op Fl admopstux .Op Fl g Ar groups .Op Fl l Ar logins .Sh DESCRIPTION The .Nm utility displays information about user and system accounts. .Pp The following options are available: .Bl -tag -width ".Fl g Ar groups" .It Fl a Display information about the password change and account expiration times for each account. .It Fl d Select accounts with duplicate UIDs. .It Fl g Ar groups Select accounts that are members of the specified groups. If multiple group names are specified, they must be separated with commas. .It Fl l Ar logins Select accounts matching the specified login names. If multiple names are specified, they must be separated with commas. .It Fl m Show information about secondary groups. .It Fl o Display the information for each account on a single line of colon-separated fields. .It Fl p Select accounts with no password. .It Fl s Select system accounts. These are currently defined as accounts with UIDs below 1000, plus the .Dq Li nobody account (UID 65534). .It Fl t Sort selected accounts by name rather than by UID. .It Fl u Select user accounts. These are currently defined as accounts with UIDs above 1000, except the .Dq Li nobody account (UID 65534). .It Fl x Display information about each account's home directory and shell. .El .Pp If multiple selection options are specified, all accounts matching any of the selection criteria will be displayed. .Pp If no selection options are specified, all accounts will be displayed. .Sh SEE ALSO .Xr getgrent 3 , .Xr getpwent 3 , .Xr group 5 , .Xr passwd 5 , .Xr pw 8 .Sh HISTORY The .Nm utility appeared in .Fx 4.10 . .Sh AUTHORS The .Nm utility was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org based on similar utilities in other operating systems. diff --git a/usr.bin/logins/logins.c b/usr.bin/logins/logins.c index aef7eb7aba5b..683abf321547 100644 --- a/usr.bin/logins/logins.c +++ b/usr.bin/logins/logins.c @@ -1,406 +1,406 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2004 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include struct xpasswd { char *pw_name; char *pw_passwd; uid_t pw_uid; gid_t pw_gid; time_t pw_change; char *pw_class; char *pw_gecos; char *pw_dir; char *pw_shell; time_t pw_expire; int pw_selected; }; struct xgroup { char *gr_name; char *gr_passwd; gid_t gr_gid; char *gr_mem; }; static int everything = 1; static int a_flag; static int d_flag; static const char *g_args; static const char *l_args; static int m_flag; static int o_flag; static int p_flag; static int s_flag; static int t_flag; static int u_flag; static int x_flag; static int member(const char *elem, const char *list) { char *p; int len; p = strstr(list, elem); len = strlen(elem); return (p != NULL && (p == list || p[-1] == ',') && (p[len] == '\0' || p[len] == ',')); } static void * xmalloc(size_t size) { void *newptr; if ((newptr = malloc(size)) == NULL) err(1, "malloc()"); return (newptr); } static void * xrealloc(void *ptr, size_t size) { void *newptr; if ((newptr = realloc(ptr, size)) == NULL) err(1, "realloc()"); return (newptr); } static char * xstrdup(const char *str) { char *dupstr; if ((dupstr = strdup(str)) == NULL) err(1, "strdup()"); return (dupstr); } static struct xgroup *grps; static size_t grpsz; static size_t ngrps; static void get_groups(void) { struct group *grp; size_t len; int i; setgrent(); for (;;) { if (ngrps == grpsz) { grpsz += grpsz ? grpsz : 128; grps = xrealloc(grps, grpsz * sizeof *grps); } if ((grp = getgrent()) == NULL) break; grps[ngrps].gr_name = xstrdup(grp->gr_name); grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); grps[ngrps].gr_gid = grp->gr_gid; grps[ngrps].gr_mem = xstrdup(""); for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) len += strlen(grp->gr_mem[i]) + 1; grps[ngrps].gr_mem = xmalloc(len); for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) len += sprintf(grps[ngrps].gr_mem + len, i ? ",%s" : "%s", grp->gr_mem[i]); grps[ngrps].gr_mem[len] = '\0'; ngrps++; } endgrent(); } static struct xgroup * find_group_bygid(gid_t gid) { unsigned int i; for (i = 0; i < ngrps; ++i) if (grps[i].gr_gid == gid) return (&grps[i]); return (NULL); } #if 0 static struct xgroup * find_group_byname(const char *name) { unsigned int i; for (i = 0; i < ngrps; ++i) if (strcmp(grps[i].gr_name, name) == 0) return (&grps[i]); return (NULL); } #endif static struct xpasswd *pwds; static size_t pwdsz; static size_t npwds; static int pwd_cmp_byname(const void *ap, const void *bp) { const struct passwd *a = ap; const struct passwd *b = bp; return (strcmp(a->pw_name, b->pw_name)); } static int pwd_cmp_byuid(const void *ap, const void *bp) { const struct passwd *a = ap; const struct passwd *b = bp; return (a->pw_uid - b->pw_uid); } static void get_users(void) { struct passwd *pwd; setpwent(); for (;;) { if (npwds == pwdsz) { pwdsz += pwdsz ? pwdsz : 128; pwds = xrealloc(pwds, pwdsz * sizeof *pwds); } if ((pwd = getpwent()) == NULL) break; pwds[npwds].pw_name = xstrdup(pwd->pw_name); pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); pwds[npwds].pw_uid = pwd->pw_uid; pwds[npwds].pw_gid = pwd->pw_gid; pwds[npwds].pw_change = pwd->pw_change; pwds[npwds].pw_class = xstrdup(pwd->pw_class); pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); pwds[npwds].pw_expire = pwd->pw_expire; pwds[npwds].pw_selected = 0; npwds++; } endpwent(); } static void select_users(void) { unsigned int i, j; struct xgroup *grp; struct xpasswd *pwd; for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { if (everything) { pwd->pw_selected = 1; continue; } if (d_flag) if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { pwd->pw_selected = 1; continue; } if (g_args) { for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { if (member(grp->gr_name, g_args) && member(pwd->pw_name, grp->gr_mem)) { pwd->pw_selected = 1; break; } } if (pwd->pw_selected) continue; } if (l_args) if (member(pwd->pw_name, l_args)) { pwd->pw_selected = 1; continue; } if (p_flag) if (pwd->pw_passwd[0] == '\0') { pwd->pw_selected = 1; continue; } if (s_flag) if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { pwd->pw_selected = 1; continue; } if (u_flag) if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { pwd->pw_selected = 1; continue; } } } static void sort_users(void) { if (t_flag) mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); else mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); } static void display_user(struct xpasswd *pwd) { struct xgroup *grp; unsigned int i; char cbuf[16], ebuf[16]; struct tm *tm; grp = find_group_bygid(pwd->pw_gid); printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", (long)pwd->pw_gid, pwd->pw_gecos); if (m_flag) { for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { if (grp->gr_gid == pwd->pw_gid || !member(pwd->pw_name, grp->gr_mem)) continue; printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", "", grp->gr_name, (long)grp->gr_gid); } } if (x_flag) { printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); } if (a_flag) { tm = gmtime(&pwd->pw_change); strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); tm = gmtime(&pwd->pw_expire); strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); } if (o_flag) printf("\n"); } static void list_users(void) { struct xpasswd *pwd; unsigned int i; for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) if (pwd->pw_selected) display_user(pwd); } static void usage(void) { fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); exit(1); } int main(int argc, char * const argv[]) { int o; while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) switch (o) { case 'a': a_flag = 1; break; case 'd': everything = 0; d_flag = 1; break; case 'g': everything = 0; g_args = optarg; break; case 'l': everything = 0; l_args = optarg; break; case 'm': m_flag = 1; break; case 'o': o_flag = 1; break; case 'p': everything = 0; p_flag = 1; break; case 's': everything = 0; s_flag = 1; break; case 't': t_flag = 1; break; case 'u': everything = 0; u_flag = 1; break; case 'x': x_flag = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) usage(); get_groups(); get_users(); select_users(); sort_users(); list_users(); exit(0); } diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1 index e6eb67c2e5a2..2e1b06688afa 100644 --- a/usr.bin/sockstat/sockstat.1 +++ b/usr.bin/sockstat/sockstat.1 @@ -1,236 +1,236 @@ .\"- -.\" Copyright (c) 1999 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 1999 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer .\" in this position and unchanged. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd June 6, 2022 .Dt SOCKSTAT 1 .Os .Sh NAME .Nm sockstat .Nd list open sockets .Sh SYNOPSIS .Nm .Op Fl 46CciLlnqSsUuvw .Op Fl j Ar jail .Op Fl p Ar ports .Op Fl P Ar protocols .Sh DESCRIPTION The .Nm command lists open Internet or .Ux domain sockets. .Pp The following options are available: .Bl -tag -width Fl .It Fl 4 Show .Dv AF_INET (IPv4) sockets. .It Fl 6 Show .Dv AF_INET6 (IPv6) sockets. .It Fl C Display the congestion control module, if applicable. This is currently only implemented for TCP. .It Fl c Show connected sockets. .It Fl i Display the .Dv inp_gencnt . .It Fl j Ar jail Show only sockets belonging to the specified jail ID or name. .It Fl L Only show Internet sockets if the local and foreign addresses are not in the loopback network prefix .Li 127.0.0.0/8 , or do not contain the IPv6 loopback address .Li ::1 . .It Fl l Show listening sockets. .It Fl n Do not resolve numeric UIDs to user names. .It Fl p Ar ports Only show Internet sockets if the local or foreign port number is on the specified list. The .Ar ports argument is a comma-separated list of port numbers and ranges specified as first and last port separated by a dash. .It Fl P Ar protocols Only show sockets of the specified .Ar protocols . The .Ar protocols argument is a comma-separated list of protocol names, as they are defined in .Xr protocols 5 . .It Fl q Quiet mode, do not print the header line. .It Fl S Display the protocol stack, if applicable. This is currently only implemented for TCP. .It Fl s Display the protocol state, if applicable. This is currently only implemented for SCTP and TCP. .It Fl U Display the remote UDP encapsulation port number, if applicable. This is currently only implemented for SCTP and TCP. .It Fl u Show .Dv AF_LOCAL .Pq Ux sockets. .It Fl v Verbose mode. .It Fl w Use wider field size for displaying addresses. .El .Pp If neither .Fl 4 , 6 or .Fl u is specified, .Nm will list sockets in all three domains. .Pp If neither .Fl c or .Fl l is specified, .Nm will list both listening and connected sockets. .Pp The information listed for each socket is: .Bl -tag -width "FOREIGN ADDRESS" .It Li USER The user who owns the socket. .It Li COMMAND The command which holds the socket. .It Li PID The process ID of the command which holds the socket. .It Li FD The file descriptor number of the socket. .It Li PROTO The transport protocol associated with the socket for Internet sockets, or the type of socket .Pq stream, datagram, or seqpacket for .Ux sockets. .It Li LOCAL ADDRESS For Internet sockets, this is the address the local end of the socket is bound to (see .Xr getsockname 2 ) . .Pp For bound .Ux sockets, socket's filename is printed. For not bound .Ux sockets, the field is empty. .It Li FOREIGN ADDRESS For Internet sockets, this is the address the foreign end of the socket is bound to (see .Xr getpeername 2 ) . .Pp For bound .Ux sockets a left arrow followed by the peer list is printed. For .Ux sockets that went through .Xr connect 2 system call a right arrow followed by the peer is printed. Peers are printed in square brackets as [PID FD]. .It Li ID The inp_gencnt if .Fl i is specified (only for TCP or UDP). .It Li ENCAPS The remote UDP encapsulation port number if .Fl U is specified (only for SCTP or TCP). .It Li PATH STATE The path state if .Fl s is specified (only for SCTP). .It Li CONN STATE The connection state if .Fl s is specified (only for SCTP or TCP). .It Li STACK The protocol stack if .Fl S is specified (only for TCP). .It Li CC The congestion control if .Fl C is specified (only for TCP). .El .Pp If a socket is associated with more than one file descriptor, it is shown multiple times. If a socket is not associated with any file descriptor, the first four columns have no meaning. .Sh EXAMPLES Show information for IPv4 sockets listening on port 22 using protocol TCP: .Bd -literal -offset indent $ sockstat -4 -l -P tcp -p 22 .Ed .Pp Show information for sockets using either TCP or UDP, if neither, the local nor the foreign addresses are in the loopback network: .Bd -literal -offset indent $ sockstat -L -P tcp,udp .Ed .Pp Show TCP IPv6 sockets which are listening and connected (default): .Bd -literal -offset indent $ sockstat -6 -P tcp .Ed .Sh SEE ALSO .Xr fstat 1 , .Xr netstat 1 , .Xr procstat 1 , .Xr inet 4 , .Xr inet6 4 , .Xr protocols 5 .Sh HISTORY The .Nm command appeared in .Fx 3.1 . .Sh AUTHORS The .Nm command and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c index 378fbbf0fef5..52c494f03045 100644 --- a/usr.bin/sockstat/sockstat.c +++ b/usr.bin/sockstat/sockstat.c @@ -1,1579 +1,1579 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2002 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TCPSTATES /* load state names */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define sstosin(ss) ((struct sockaddr_in *)(ss)) #define sstosin6(ss) ((struct sockaddr_in6 *)(ss)) #define sstosun(ss) ((struct sockaddr_un *)(ss)) #define sstosa(ss) ((struct sockaddr *)(ss)) static int opt_4; /* Show IPv4 sockets */ static int opt_6; /* Show IPv6 sockets */ static int opt_C; /* Show congestion control */ static int opt_c; /* Show connected sockets */ static int opt_i; /* Show inp_gencnt */ static int opt_j; /* Show specified jail */ static int opt_L; /* Don't show IPv4 or IPv6 loopback sockets */ static int opt_l; /* Show listening sockets */ static int opt_n; /* Don't resolve UIDs to user names */ static int opt_q; /* Don't show header */ static int opt_S; /* Show protocol stack if applicable */ static int opt_s; /* Show protocol state if applicable */ static int opt_U; /* Show remote UDP encapsulation port number */ static int opt_u; /* Show Unix domain sockets */ static int opt_v; /* Verbose mode */ static int opt_w; /* Wide print area for addresses */ /* * Default protocols to use if no -P was defined. */ static const char *default_protos[] = {"sctp", "tcp", "udp", "divert" }; static size_t default_numprotos = nitems(default_protos); static int *protos; /* protocols to use */ static size_t numprotos; /* allocated size of protos[] */ static int *ports; #define INT_BIT (sizeof(int)*CHAR_BIT) #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0) #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT))) struct addr { union { struct sockaddr_storage address; struct { /* unix(4) faddr */ kvaddr_t conn; kvaddr_t firstref; kvaddr_t nextref; }; }; unsigned int encaps_port; int state; struct addr *next; }; struct sock { union { RB_ENTRY(sock) socket_tree; /* tree of pcbs with socket */ SLIST_ENTRY(sock) socket_list; /* list of pcbs w/o socket */ }; RB_ENTRY(sock) pcb_tree; kvaddr_t socket; kvaddr_t pcb; uint64_t inp_gencnt; int shown; int vflag; int family; int proto; int state; const char *protoname; char stack[TCP_FUNCTION_NAME_LEN_MAX]; char cc[TCP_CA_NAME_MAX]; struct addr *laddr; struct addr *faddr; }; static RB_HEAD(socks_t, sock) socks = RB_INITIALIZER(&socks); static int64_t socket_compare(const struct sock *a, const struct sock *b) { return ((int64_t)(a->socket/2 - b->socket/2)); } RB_GENERATE_STATIC(socks_t, sock, socket_tree, socket_compare); static RB_HEAD(pcbs_t, sock) pcbs = RB_INITIALIZER(&pcbs); static int64_t pcb_compare(const struct sock *a, const struct sock *b) { return ((int64_t)(a->pcb/2 - b->pcb/2)); } RB_GENERATE_STATIC(pcbs_t, sock, pcb_tree, pcb_compare); static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks); struct file { RB_ENTRY(file) file_tree; kvaddr_t xf_data; pid_t xf_pid; uid_t xf_uid; int xf_fd; }; static RB_HEAD(files_t, file) ftree = RB_INITIALIZER(&ftree); static int64_t file_compare(const struct file *a, const struct file *b) { return ((int64_t)(a->xf_data/2 - b->xf_data/2)); } RB_GENERATE_STATIC(files_t, file, file_tree, file_compare); static struct file *files; static int nfiles; static cap_channel_t *capnet; static cap_channel_t *capnetdb; static cap_channel_t *capsysctl; static cap_channel_t *cappwd; static int xprintf(const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = vprintf(fmt, ap); va_end(ap); if (len < 0) err(1, "printf()"); return (len); } static bool _check_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { warnx("%s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); return false; } return true; } #define check_ksize(_sz, _struct) (_check_ksize(_sz, sizeof(_struct), #_struct)) static void _enforce_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { errx(1, "fatal: struct %s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); } } #define enforce_ksize(_sz, _struct) (_enforce_ksize(_sz, sizeof(_struct), #_struct)) static int get_proto_type(const char *proto) { struct protoent *pent; if (strlen(proto) == 0) return (0); if (capnetdb != NULL) pent = cap_getprotobyname(capnetdb, proto); else pent = getprotobyname(proto); if (pent == NULL) { warn("cap_getprotobyname"); return (-1); } return (pent->p_proto); } static void init_protos(int num) { int proto_count = 0; if (num > 0) { proto_count = num; } else { /* Find the maximum number of possible protocols. */ while (getprotoent() != NULL) proto_count++; endprotoent(); } if ((protos = malloc(sizeof(int) * proto_count)) == NULL) err(1, "malloc"); numprotos = proto_count; } static int parse_protos(char *protospec) { char *prot; int proto_type, proto_index; if (protospec == NULL) return (-1); init_protos(0); proto_index = 0; while ((prot = strsep(&protospec, ",")) != NULL) { if (strlen(prot) == 0) continue; proto_type = get_proto_type(prot); if (proto_type != -1) protos[proto_index++] = proto_type; } numprotos = proto_index; return (proto_index); } static void parse_ports(const char *portspec) { const char *p, *q; int port, end; if (ports == NULL) if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL) err(1, "calloc()"); p = portspec; while (*p != '\0') { if (!isdigit(*p)) errx(1, "syntax error in port range"); for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (port = 0; p < q; ++p) port = port * 10 + digittoint(*p); if (port < 0 || port > 65535) errx(1, "invalid port number"); SET_PORT(port); switch (*p) { case '-': ++p; break; case ',': ++p; /* fall through */ case '\0': default: continue; } for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (end = 0; p < q; ++p) end = end * 10 + digittoint(*p); if (end < port || end > 65535) errx(1, "invalid port number"); while (port++ < end) SET_PORT(port); if (*p == ',') ++p; } } static void sockaddr(struct sockaddr_storage *ss, int af, void *addr, int port) { struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; bzero(ss, sizeof(*ss)); switch (af) { case AF_INET: sin4 = sstosin(ss); sin4->sin_len = sizeof(*sin4); sin4->sin_family = af; sin4->sin_port = port; sin4->sin_addr = *(struct in_addr *)addr; break; case AF_INET6: sin6 = sstosin6(ss); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = af; sin6->sin6_port = port; sin6->sin6_addr = *(struct in6_addr *)addr; #define s6_addr16 __u6_addr.__u6_addr16 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); sin6->sin6_addr.s6_addr16[1] = 0; } break; default: abort(); } } static void free_socket(struct sock *sock) { struct addr *cur, *next; cur = sock->laddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } cur = sock->faddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } free(sock); } static void gather_sctp(void) { struct sock *sock; struct addr *laddr, *prev_laddr, *faddr, *prev_faddr; struct xsctp_inpcb *xinpcb; struct xsctp_tcb *xstcb; struct xsctp_raddr *xraddr; struct xsctp_laddr *xladdr; const char *varname; size_t len, offset; char *buf; int vflag; int no_stcb, local_all_loopback, foreign_all_loopback; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; varname = "net.inet.sctp.assoclist"; if (cap_sysctlbyname(capsysctl, varname, 0, &len, 0, 0) < 0) { if (errno != ENOENT) err(1, "cap_sysctlbyname()"); return; } if ((buf = (char *)malloc(len)) == NULL) { err(1, "malloc()"); return; } if (cap_sysctlbyname(capsysctl, varname, buf, &len, 0, 0) < 0) { err(1, "cap_sysctlbyname()"); free(buf); return; } xinpcb = (struct xsctp_inpcb *)(void *)buf; offset = sizeof(struct xsctp_inpcb); while ((offset < len) && (xinpcb->last == 0)) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; if (xinpcb->maxqlen == 0) sock->state = SCTP_CLOSED; else sock->state = SCTP_LISTEN; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; /* * Currently there is no way to distinguish between * IPv6 only sockets or dual family sockets. * So mark it as dual socket. */ sock->vflag = INP_IPV6 | INP_IPV4; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if ((laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xinpcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xinpcb->local_port)); break; default: errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } if (sock->laddr == NULL) { if ((sock->laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->laddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->laddr->address.ss_len = sizeof(struct sockaddr_in); else sock->laddr->address.ss_len = sizeof(struct sockaddr_in6); local_all_loopback = 0; } if ((sock->faddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->faddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->faddr->address.ss_len = sizeof(struct sockaddr_in); else sock->faddr->address.ss_len = sizeof(struct sockaddr_in6); no_stcb = 1; while (offset < len) { xstcb = (struct xsctp_tcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_tcb); if (no_stcb) { if (opt_l && (sock->vflag & vflag) && (!opt_L || !local_all_loopback) && ((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) || (xstcb->last == 1))) { RB_INSERT(socks_t, &socks, sock); } else { free_socket(sock); } } if (xstcb->last == 1) break; no_stcb = 0; if (opt_c) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; sock->state = (int)xstcb->state; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; /* * Currently there is no way to distinguish * between IPv6 only sockets or dual family * sockets. So mark it as dual socket. */ sock->vflag = INP_IPV6 | INP_IPV4; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if (!opt_c) continue; laddr = calloc(1, sizeof(struct addr)); if (laddr == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xstcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xstcb->local_port)); break; default: errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } prev_faddr = NULL; foreign_all_loopback = 1; while (offset < len) { xraddr = (struct xsctp_raddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_raddr); if (xraddr->last == 1) break; if (!opt_c) continue; faddr = calloc(1, sizeof(struct addr)); if (faddr == NULL) err(1, "malloc()"); switch (xraddr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xraddr->address.sin.sin_addr)) foreign_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&faddr->address, AF_INET, &xraddr->address.sin.sin_addr, htons(xstcb->remote_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xraddr->address.sin6.sin6_addr)) foreign_all_loopback = 0; sockaddr(&faddr->address, AF_INET6, &xraddr->address.sin6.sin6_addr, htons(xstcb->remote_port)); break; default: errx(1, "address family %d not supported", xraddr->address.sa.sa_family); } faddr->encaps_port = xraddr->encaps_port; faddr->state = xraddr->state; faddr->next = NULL; if (prev_faddr == NULL) sock->faddr = faddr; else prev_faddr->next = faddr; prev_faddr = faddr; } if (opt_c) { if ((sock->vflag & vflag) && (!opt_L || !(local_all_loopback || foreign_all_loopback))) { RB_INSERT(socks_t, &socks, sock); } else { free_socket(sock); } } } xinpcb = (struct xsctp_inpcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_inpcb); } free(buf); } static void gather_inet(int proto) { struct xinpgen *xig, *exig; struct xinpcb *xip; struct xtcpcb *xtp = NULL; struct xsocket *so; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int retry, vflag; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; switch (proto) { case IPPROTO_TCP: varname = "net.inet.tcp.pcblist"; protoname = "tcp"; break; case IPPROTO_UDP: varname = "net.inet.udp.pcblist"; protoname = "udp"; break; case IPPROTO_DIVERT: varname = "net.inet.divert.pcblist"; protoname = "div"; break; default: errx(1, "protocol %d not supported", proto); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) break; if (errno == ENOENT) goto out; if (errno != ENOMEM || len != bufsize) err(1, "cap_sysctlbyname()"); bufsize *= 2; } xig = (struct xinpgen *)buf; exig = (struct xinpgen *)(void *) ((char *)buf + len - sizeof *exig); enforce_ksize(xig->xig_len, struct xinpgen); enforce_ksize(exig->xig_len, struct xinpgen); } while (xig->xig_gen != exig->xig_gen && retry--); if (xig->xig_gen != exig->xig_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); if (xig >= exig) break; switch (proto) { case IPPROTO_TCP: xtp = (struct xtcpcb *)xig; xip = &xtp->xt_inp; if (!check_ksize(xtp->xt_len, struct xtcpcb)) goto out; protoname = xtp->t_flags & TF_TOE ? "toe" : "tcp"; break; case IPPROTO_UDP: case IPPROTO_DIVERT: xip = (struct xinpcb *)xig; if (!check_ksize(xip->xi_len, struct xinpcb)) goto out; break; default: errx(1, "protocol %d not supported", proto); } so = &xip->xi_socket; if ((xip->inp_vflag & vflag) == 0) continue; if (xip->inp_vflag & INP_IPV4) { if ((xip->inp_fport == 0 && !opt_l) || (xip->inp_fport != 0 && !opt_c)) continue; #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (opt_L && (__IN_IS_ADDR_LOOPBACK(&xip->inp_faddr) || __IN_IS_ADDR_LOOPBACK(&xip->inp_laddr))) continue; #undef __IN_IS_ADDR_LOOPBACK } else if (xip->inp_vflag & INP_IPV6) { if ((xip->inp_fport == 0 && !opt_l) || (xip->inp_fport != 0 && !opt_c)) continue; if (opt_L && (IN6_IS_ADDR_LOOPBACK(&xip->in6p_faddr) || IN6_IS_ADDR_LOOPBACK(&xip->in6p_laddr))) continue; } else { if (opt_v) warnx("invalid vflag 0x%x", xip->inp_vflag); continue; } if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = so->xso_so; sock->proto = proto; sock->inp_gencnt = xip->inp_gencnt; if (xip->inp_vflag & INP_IPV4) { sock->family = AF_INET; sockaddr(&laddr->address, sock->family, &xip->inp_laddr, xip->inp_lport); sockaddr(&faddr->address, sock->family, &xip->inp_faddr, xip->inp_fport); } else if (xip->inp_vflag & INP_IPV6) { sock->family = AF_INET6; sockaddr(&laddr->address, sock->family, &xip->in6p_laddr, xip->inp_lport); sockaddr(&faddr->address, sock->family, &xip->in6p_faddr, xip->inp_fport); } if (proto == IPPROTO_TCP) faddr->encaps_port = xtp->xt_encaps_port; laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; sock->vflag = xip->inp_vflag; if (proto == IPPROTO_TCP) { sock->state = xtp->t_state; memcpy(sock->stack, xtp->xt_stack, TCP_FUNCTION_NAME_LEN_MAX); memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX); } sock->protoname = protoname; if (sock->socket != 0) RB_INSERT(socks_t, &socks, sock); else SLIST_INSERT_HEAD(&nosocks, sock, socket_list); } out: free(buf); } static void gather_unix(int proto) { struct xunpgen *xug, *exug; struct xunpcb *xup; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int retry; switch (proto) { case SOCK_STREAM: varname = "net.local.stream.pcblist"; protoname = "stream"; break; case SOCK_DGRAM: varname = "net.local.dgram.pcblist"; protoname = "dgram"; break; case SOCK_SEQPACKET: varname = "net.local.seqpacket.pcblist"; protoname = "seqpac"; break; default: abort(); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) break; if (errno != ENOMEM || len != bufsize) err(1, "cap_sysctlbyname()"); bufsize *= 2; } xug = (struct xunpgen *)buf; exug = (struct xunpgen *)(void *) ((char *)buf + len - sizeof(*exug)); if (!check_ksize(xug->xug_len, struct xunpgen) || !check_ksize(exug->xug_len, struct xunpgen)) goto out; } while (xug->xug_gen != exug->xug_gen && retry--); if (xug->xug_gen != exug->xug_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); if (xug >= exug) break; xup = (struct xunpcb *)xug; if (!check_ksize(xup->xu_len, struct xunpcb)) goto out; if ((xup->unp_conn == 0 && !opt_l) || (xup->unp_conn != 0 && !opt_c)) continue; if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = xup->xu_socket.xso_so; sock->pcb = xup->xu_unpp; sock->proto = proto; sock->family = AF_UNIX; sock->protoname = protoname; if (xup->xu_addr.sun_family == AF_UNIX) laddr->address = *(struct sockaddr_storage *)(void *)&xup->xu_addr; faddr->conn = xup->unp_conn; faddr->firstref = xup->xu_firstref; faddr->nextref = xup->xu_nextref; laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; RB_INSERT(socks_t, &socks, sock); RB_INSERT(pcbs_t, &pcbs, sock); } out: free(buf); } static void getfiles(void) { struct xfile *xfiles; size_t len, olen; olen = len = sizeof(*xfiles); if ((xfiles = malloc(len)) == NULL) err(1, "malloc()"); while (cap_sysctlbyname(capsysctl, "kern.file", xfiles, &len, 0, 0) == -1) { if (errno != ENOMEM || len != olen) err(1, "cap_sysctlbyname()"); olen = len *= 2; if ((xfiles = realloc(xfiles, len)) == NULL) err(1, "realloc()"); } if (len > 0) enforce_ksize(xfiles->xf_size, struct xfile); nfiles = len / sizeof(*xfiles); if ((files = malloc(nfiles * sizeof(struct file))) == NULL) err(1, "malloc()"); for (int i = 0; i < nfiles; i++) { files[i].xf_data = xfiles[i].xf_data; files[i].xf_pid = xfiles[i].xf_pid; files[i].xf_uid = xfiles[i].xf_uid; files[i].xf_fd = xfiles[i].xf_fd; RB_INSERT(files_t, &ftree, &files[i]); } free(xfiles); } static int printaddr(struct sockaddr_storage *ss) { struct sockaddr_un *sun; char addrstr[NI_MAXHOST] = { '\0', '\0' }; int error, off, port = 0; switch (ss->ss_family) { case AF_INET: if (sstosin(ss)->sin_addr.s_addr == INADDR_ANY) addrstr[0] = '*'; port = ntohs(sstosin(ss)->sin_port); break; case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&sstosin6(ss)->sin6_addr)) addrstr[0] = '*'; port = ntohs(sstosin6(ss)->sin6_port); break; case AF_UNIX: sun = sstosun(ss); off = (int)((char *)&sun->sun_path - (char *)sun); return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); } if (addrstr[0] == '\0') { error = cap_getnameinfo(capnet, sstosa(ss), ss->ss_len, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST); if (error) errx(1, "cap_getnameinfo()"); } if (port == 0) return xprintf("%s:*", addrstr); else return xprintf("%s:%d", addrstr, port); } static const char * getprocname(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its name. */ if (errno != ESRCH) warn("cap_sysctl()"); return ("??"); } return (proc.ki_comm); } static int getprocjid(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its jid. */ if (errno != ESRCH) warn("cap_sysctl()"); return (-1); } return (proc.ki_jid); } static int check_ports(struct sock *s) { int port; struct addr *addr; if (ports == NULL) return (1); if ((s->family != AF_INET) && (s->family != AF_INET6)) return (1); for (addr = s->laddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } for (addr = s->faddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } return (0); } static const char * sctp_conn_state(int state) { switch (state) { case SCTP_CLOSED: return "CLOSED"; break; case SCTP_BOUND: return "BOUND"; break; case SCTP_LISTEN: return "LISTEN"; break; case SCTP_COOKIE_WAIT: return "COOKIE_WAIT"; break; case SCTP_COOKIE_ECHOED: return "COOKIE_ECHOED"; break; case SCTP_ESTABLISHED: return "ESTABLISHED"; break; case SCTP_SHUTDOWN_SENT: return "SHUTDOWN_SENT"; break; case SCTP_SHUTDOWN_RECEIVED: return "SHUTDOWN_RECEIVED"; break; case SCTP_SHUTDOWN_ACK_SENT: return "SHUTDOWN_ACK_SENT"; break; case SCTP_SHUTDOWN_PENDING: return "SHUTDOWN_PENDING"; break; default: return "UNKNOWN"; break; } } static const char * sctp_path_state(int state) { switch (state) { case SCTP_UNCONFIRMED: return "UNCONFIRMED"; break; case SCTP_ACTIVE: return "ACTIVE"; break; case SCTP_INACTIVE: return "INACTIVE"; break; default: return "UNKNOWN"; break; } } static void displaysock(struct sock *s, int pos) { int first, offset; struct addr *laddr, *faddr; while (pos < 30) pos += xprintf(" "); pos += xprintf("%s", s->protoname); if (s->vflag & INP_IPV4) pos += xprintf("4"); if (s->vflag & INP_IPV6) pos += xprintf("6"); if (s->vflag & (INP_IPV4 | INP_IPV6)) pos += xprintf(" "); laddr = s->laddr; faddr = s->faddr; first = 1; while (laddr != NULL || faddr != NULL) { offset = 37; while (pos < offset) pos += xprintf(" "); switch (s->family) { case AF_INET: case AF_INET6: if (laddr != NULL) { pos += printaddr(&laddr->address); if (s->family == AF_INET6 && pos >= 58) pos += xprintf(" "); } offset += opt_w ? 46 : 22; while (pos < offset) pos += xprintf(" "); if (faddr != NULL) pos += printaddr(&faddr->address); offset += opt_w ? 46 : 22; break; case AF_UNIX: if ((laddr == NULL) || (faddr == NULL)) errx(1, "laddr = %p or faddr = %p is NULL", (void *)laddr, (void *)faddr); if (laddr->address.ss_len == 0 && faddr->conn == 0) { pos += xprintf("(not connected)"); offset += opt_w ? 92 : 44; break; } /* Local bind(2) address, if any. */ if (laddr->address.ss_len > 0) pos += printaddr(&laddr->address); /* Remote peer we connect(2) to, if any. */ if (faddr->conn != 0) { struct sock *p; pos += xprintf("%s-> ", laddr->address.ss_len > 0 ? " " : ""); p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = faddr->conn }); if (__predict_false(p == NULL)) { /* XXGL: can this happen at all? */ pos += xprintf("??"); } else if (p->laddr->address.ss_len == 0) { struct file *f; f = RB_FIND(files_t, &ftree, &(struct file){ .xf_data = p->socket }); pos += xprintf("[%lu %d]", (u_long)f->xf_pid, f->xf_fd); } else pos += printaddr(&p->laddr->address); } /* Remote peer(s) connect(2)ed to us, if any. */ if (faddr->firstref != 0) { struct sock *p; struct file *f; kvaddr_t ref = faddr->firstref; bool fref = true; pos += xprintf(" <- "); while ((p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = ref })) != 0) { f = RB_FIND(files_t, &ftree, &(struct file){ .xf_data = p->socket }); pos += xprintf("%s[%lu %d]", fref ? "" : ",", (u_long)f->xf_pid, f->xf_fd); ref = p->faddr->nextref; fref = false; } } offset += opt_w ? 92 : 44; break; default: abort(); } if (opt_i) { if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%" PRIu64, s->inp_gencnt); } offset += 9; } if (opt_U) { if (faddr != NULL && ((s->proto == IPPROTO_SCTP && s->state != SCTP_CLOSED && s->state != SCTP_BOUND && s->state != SCTP_LISTEN) || (s->proto == IPPROTO_TCP && s->state != TCPS_CLOSED && s->state != TCPS_LISTEN))) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%u", ntohs(faddr->encaps_port)); } offset += 7; } if (opt_s) { if (faddr != NULL && s->proto == IPPROTO_SCTP && s->state != SCTP_CLOSED && s->state != SCTP_BOUND && s->state != SCTP_LISTEN) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%s", sctp_path_state(faddr->state)); } offset += 13; } if (first) { if (opt_s) { if (s->proto == IPPROTO_SCTP || s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); switch (s->proto) { case IPPROTO_SCTP: pos += xprintf("%s", sctp_conn_state(s->state)); break; case IPPROTO_TCP: if (s->state >= 0 && s->state < TCP_NSTATES) pos += xprintf("%s", tcpstates[s->state]); else pos += xprintf("?"); break; } } offset += 13; } if (opt_S) { if (s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%.*s", TCP_FUNCTION_NAME_LEN_MAX, s->stack); } offset += TCP_FUNCTION_NAME_LEN_MAX + 1; } if (opt_C) { if (s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); xprintf("%.*s", TCP_CA_NAME_MAX, s->cc); } offset += TCP_CA_NAME_MAX + 1; } } if (laddr != NULL) laddr = laddr->next; if (faddr != NULL) faddr = faddr->next; if ((laddr != NULL) || (faddr != NULL)) { xprintf("\n"); pos = 0; } first = 0; } xprintf("\n"); } static void display(void) { struct passwd *pwd; struct file *xf; struct sock *s; int n, pos; if (opt_q != 1) { printf("%-8s %-10s %-5s %-3s %-6s %-*s %-*s", "USER", "COMMAND", "PID", "FD", "PROTO", opt_w ? 45 : 21, "LOCAL ADDRESS", opt_w ? 45 : 21, "FOREIGN ADDRESS"); if (opt_i) printf(" %-8s", "ID"); if (opt_U) printf(" %-6s", "ENCAPS"); if (opt_s) { printf(" %-12s", "PATH STATE"); printf(" %-12s", "CONN STATE"); } if (opt_S) printf(" %-*.*s", TCP_FUNCTION_NAME_LEN_MAX, TCP_FUNCTION_NAME_LEN_MAX, "STACK"); if (opt_C) printf(" %-.*s", TCP_CA_NAME_MAX, "CC"); printf("\n"); } cap_setpassent(cappwd, 1); for (xf = files, n = 0; n < nfiles; ++n, ++xf) { if (xf->xf_data == 0) continue; if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid)) continue; s = RB_FIND(socks_t, &socks, &(struct sock){ .socket = xf->xf_data}); if (s != NULL && check_ports(s)) { s->shown = 1; pos = 0; if (opt_n || (pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL) pos += xprintf("%lu ", (u_long)xf->xf_uid); else pos += xprintf("%s ", pwd->pw_name); while (pos < 9) pos += xprintf(" "); pos += xprintf("%.10s", getprocname(xf->xf_pid)); while (pos < 20) pos += xprintf(" "); pos += xprintf("%5lu ", (u_long)xf->xf_pid); while (pos < 26) pos += xprintf(" "); pos += xprintf("%-3d ", xf->xf_fd); displaysock(s, pos); } } if (opt_j >= 0) return; SLIST_FOREACH(s, &nosocks, socket_list) { if (!check_ports(s)) continue; pos = xprintf("%-8s %-10s %-5s %-2s ", "?", "?", "?", "?"); displaysock(s, pos); } RB_FOREACH(s, socks_t, &socks) { if (s->shown) continue; if (!check_ports(s)) continue; pos = xprintf("%-8s %-10s %-5s %-2s ", "?", "?", "?", "?"); displaysock(s, pos); } } static int set_default_protos(void) { struct protoent *prot; const char *pname; size_t pindex; init_protos(default_numprotos); for (pindex = 0; pindex < default_numprotos; pindex++) { pname = default_protos[pindex]; prot = cap_getprotobyname(capnetdb, pname); if (prot == NULL) err(1, "cap_getprotobyname: %s", pname); protos[pindex] = prot->p_proto; } numprotos = pindex; return (pindex); } /* * Return the vnet property of the jail, or -1 on error. */ static int jail_getvnet(int jid) { struct iovec jiov[6]; int vnet; size_t len = sizeof(vnet); if (sysctlbyname("kern.features.vimage", &vnet, &len, NULL, 0) != 0) return (0); vnet = -1; jiov[0].iov_base = __DECONST(char *, "jid"); jiov[0].iov_len = sizeof("jid"); jiov[1].iov_base = &jid; jiov[1].iov_len = sizeof(jid); jiov[2].iov_base = __DECONST(char *, "vnet"); jiov[2].iov_len = sizeof("vnet"); jiov[3].iov_base = &vnet; jiov[3].iov_len = sizeof(vnet); jiov[4].iov_base = __DECONST(char *, "errmsg"); jiov[4].iov_len = sizeof("errmsg"); jiov[5].iov_base = jail_errmsg; jiov[5].iov_len = JAIL_ERRMSGLEN; jail_errmsg[0] = '\0'; if (jail_get(jiov, nitems(jiov), 0) < 0) { if (!jail_errmsg[0]) snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno)); return (-1); } return (vnet); } static void usage(void) { fprintf(stderr, "usage: sockstat [-46CciLlnqSsUuvw] [-j jid] [-p ports] [-P protocols]\n"); exit(1); } int main(int argc, char *argv[]) { cap_channel_t *capcas; cap_net_limit_t *limit; const char *pwdcmds[] = { "setpassent", "getpwuid" }; const char *pwdfields[] = { "pw_name" }; int protos_defined = -1; int o, i; opt_j = -1; while ((o = getopt(argc, argv, "46Ccij:Llnp:P:qSsUuvw")) != -1) switch (o) { case '4': opt_4 = 1; break; case '6': opt_6 = 1; break; case 'C': opt_C = 1; break; case 'c': opt_c = 1; break; case 'i': opt_i = 1; break; case 'j': opt_j = jail_getid(optarg); if (opt_j < 0) errx(1, "jail_getid: %s", jail_errmsg); break; case 'L': opt_L = 1; break; case 'l': opt_l = 1; break; case 'n': opt_n = 1; break; case 'p': parse_ports(optarg); break; case 'P': protos_defined = parse_protos(optarg); break; case 'q': opt_q = 1; break; case 'S': opt_S = 1; break; case 's': opt_s = 1; break; case 'U': opt_U = 1; break; case 'u': opt_u = 1; break; case 'v': ++opt_v; break; case 'w': opt_w = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) usage(); if (opt_j > 0) { switch (jail_getvnet(opt_j)) { case -1: errx(2, "jail_getvnet: %s", jail_errmsg); case JAIL_SYS_NEW: if (jail_attach(opt_j) < 0) err(3, "jail_attach()"); /* Set back to -1 for normal output in vnet jail. */ opt_j = -1; break; default: break; } } capcas = cap_init(); if (capcas == NULL) err(1, "Unable to contact Casper"); if (caph_enter_casper() < 0) err(1, "Unable to enter capability mode"); capnet = cap_service_open(capcas, "system.net"); if (capnet == NULL) err(1, "Unable to open system.net service"); capnetdb = cap_service_open(capcas, "system.netdb"); if (capnetdb == NULL) err(1, "Unable to open system.netdb service"); capsysctl = cap_service_open(capcas, "system.sysctl"); if (capsysctl == NULL) err(1, "Unable to open system.sysctl service"); cappwd = cap_service_open(capcas, "system.pwd"); if (cappwd == NULL) err(1, "Unable to open system.pwd service"); cap_close(capcas); limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); if (limit == NULL) err(1, "Unable to init cap_net limits"); if (cap_net_limit(limit) < 0) err(1, "Unable to apply limits"); if (cap_pwd_limit_cmds(cappwd, pwdcmds, nitems(pwdcmds)) < 0) err(1, "Unable to apply pwd commands limits"); if (cap_pwd_limit_fields(cappwd, pwdfields, nitems(pwdfields)) < 0) err(1, "Unable to apply pwd commands limits"); if ((!opt_4 && !opt_6) && protos_defined != -1) opt_4 = opt_6 = 1; if (!opt_4 && !opt_6 && !opt_u) opt_4 = opt_6 = opt_u = 1; if ((opt_4 || opt_6) && protos_defined == -1) protos_defined = set_default_protos(); if (!opt_c && !opt_l) opt_c = opt_l = 1; if (opt_4 || opt_6) { for (i = 0; i < protos_defined; i++) if (protos[i] == IPPROTO_SCTP) gather_sctp(); else gather_inet(protos[i]); } if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) { gather_unix(SOCK_STREAM); gather_unix(SOCK_DGRAM); gather_unix(SOCK_SEQPACKET); } getfiles(); display(); exit(0); } diff --git a/usr.sbin/acpi/acpiconf/acpiconf.8 b/usr.sbin/acpi/acpiconf/acpiconf.8 index e337de5ad27c..24af8b5c8719 100644 --- a/usr.sbin/acpi/acpiconf/acpiconf.8 +++ b/usr.sbin/acpi/acpiconf/acpiconf.8 @@ -1,119 +1,119 @@ .\"- -.\" Copyright (c) 2000 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2000 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer .\" in this position and unchanged. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd June 16, 2023 .Dt ACPICONF 8 .Os .Sh NAME .Nm acpiconf .Nd control ACPI power management .Sh SYNOPSIS .Nm .Op Fl h .Op Fl i Ar batt .Op Fl k Ar ack .Op Fl s Ar type .Sh DESCRIPTION The .Nm utility allows the user control of the ACPI power management functions. The following command-line options are recognized: .Bl -tag -width ".Fl s Ar type" .It Fl h Displays a summary of available options. .It Fl i Ar batt Get design information and current status of the battery specified by its number, starting with .Cm 0 . .It Fl k Ar ack Ack or abort a pending suspend request using the argument provided. .Sy Most users should not use this option directly. .It Fl s Ar type Enters the specified sleep mode. Recognized types are .Cm 1 (only the CPU clock is stopped), .Cm 2 (not implemented on most systems but similar to S1), .Cm 3 (the CPU context is lost and memory context is preserved), and .Cm 4 (the CPU context is lost and memory context is stored to disk). Sleep states may also be given as S1, S2, etc. The supported states depend on BIOS implementation, including ACPI byte code (AML). If the .Pa /etc/rc.suspend and .Pa /etc/rc.resume scripts are executable, they will be run by .Xr devd 8 or .Xr apmd 8 before and after entering the given sleep state. .Pp The .Pa /etc/rc.suspend and .Pa /etc/rc.resume scripts use the .Xr rcorder 8 utility to call scripts in .Pa /etc/rc.d/ and the .Va $local_startup directories that have a "suspend" or "resume" KEYWORD, respectively. Called scripts are supplied with a single "suspend" / "resume" command line argument. See .Xr rc.conf 5 for more information about .Va $local_startup . .El .Sh SEE ALSO .Xr acpi 4 , .Xr rc.conf 5 , .Xr acpidump 8 , .Xr apm 8 , .Xr apmd 8 , .Xr devd 8 , .Xr rcorder 8 .Sh HISTORY The .Nm utility appeared in .Fx 5.0 . .Sh AUTHORS .An -nosplit The .Nm utility was written by .An Mitsuru Iwasaki Aq Mt iwasaki@FreeBSD.org . This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/usr.sbin/chkgrp/chkgrp.8 b/usr.sbin/chkgrp/chkgrp.8 index 20d4ee9a0349..3837e5fe6803 100644 --- a/usr.sbin/chkgrp/chkgrp.8 +++ b/usr.sbin/chkgrp/chkgrp.8 @@ -1,91 +1,91 @@ -.\" Copyright (c) 1998 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 1998 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer .\" in this position and unchanged. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd May 26, 2005 .Dt CHKGRP 8 .Os .Sh NAME .Nm chkgrp .Nd check the syntax of the group file .Sh SYNOPSIS .Nm .Op Fl q .Op Ar groupfile .Sh DESCRIPTION The .Nm utility scans the given file or, failing that, the system-wide group file for errors. Specifically, it checks that every non-blank, non-comment entry is composed of four colon-separated fields, that none of them contains whitespace, and that the third field (the group ID) is numeric. It will also check for invalid characters in the group names and group members. The following options are available: .Bl -tag -width indent .It Fl q This option disables printing of text when the group format is correct. .El .Sh FILES .Bl -tag -width /etc/group -compact .It Pa /etc/group group database file .El .Sh EXIT STATUS The .Nm utility returns .Dv EX_DATAERR if errors were found in the group file, and .Dv EX_OK otherwise. .Sh DIAGNOSTICS For each error found, .Nm will print an error message containing the name of the file being scanned and the line number on which the error was found. .Sh SEE ALSO .Xr getgrent 3 , .Xr group 5 .Sh HISTORY The .Nm utility appeared in .Fx 3.0 . .Sh AUTHORS .An -nosplit The .Nm utility and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . Further functionality was added by .An Liam J. Foy Aq Mt liamfoy@dragonflybsd.org . .Sh BUGS Should check the range of the group ID. diff --git a/usr.sbin/chkgrp/chkgrp.c b/usr.sbin/chkgrp/chkgrp.c index 650448d9e40b..69eff6e7574d 100644 --- a/usr.sbin/chkgrp/chkgrp.c +++ b/usr.sbin/chkgrp/chkgrp.c @@ -1,193 +1,193 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * - * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1998 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include static void __dead2 usage(void) { fprintf(stderr, "usage: chkgrp [-q] [groupfile]\n"); exit(EX_USAGE); } int main(int argc, char *argv[]) { FILE *gf; unsigned long gid; unsigned int i; size_t len; int opt, quiet; int n = 0, k, e = 0; const char *cp, *f[4], *gfn, *p; char *line; quiet = 0; while ((opt = getopt(argc, argv, "q")) != -1) { switch (opt) { case 'q': quiet = 1; break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) gfn = "/etc/group"; else if (argc == 1) gfn = argv[0]; else usage(); /* open group file */ if ((gf = fopen(gfn, "r")) == NULL) err(EX_NOINPUT, "%s", gfn); /* check line by line */ while (++n) { if ((line = fgetln(gf, &len)) == NULL) break; if (len > 0 && line[len - 1] != '\n') { warnx("%s: line %d: no newline character", gfn, n); e = 1; } while (len && isspace(line[len-1])) len--; /* ignore blank lines and comments */ for (p = line; p < line + len; p++) if (!isspace(*p)) break; if (!len || *p == '#') continue; /* * Hack: special case for + line */ if (strncmp(line, "+:::", len) == 0 || strncmp(line, "+:*::", len) == 0) continue; /* * A correct group entry has four colon-separated fields, * the third of which must be entirely numeric and the * fourth of which may be empty. */ for (i = k = 0; k < 4; k++) { for (f[k] = line + i; i < len && line[i] != ':'; i++) /* nothing */ ; if (k < 3 && line[i] != ':') break; line[i++] = 0; } if (k < 4) { warnx("%s: line %d: missing field(s)", gfn, n); while (k < 4) f[k++] = ""; e = 1; } for (cp = f[0] ; *cp ; cp++) { if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' && (cp > f[0] || *cp != '+')) { warnx("%s: line %d: '%c' invalid character", gfn, n, *cp); e = 1; } } for (cp = f[3] ; *cp ; cp++) { if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' && *cp != ',') { warnx("%s: line %d: '%c' invalid character", gfn, n, *cp); e = 1; } } /* check if fourth field ended with a colon */ if (i < len) { warnx("%s: line %d: too many fields", gfn, n); e = 1; } /* check that none of the fields contain whitespace */ for (k = 0; k < 4; k++) { if (strcspn(f[k], " \t") != strlen(f[k])) { warnx("%s: line %d: field %d contains whitespace", gfn, n, k+1); e = 1; } } /* check that the GID is numeric */ if (strspn(f[2], "0123456789") != strlen(f[2])) { warnx("%s: line %d: group id is not numeric", gfn, n); e = 1; } /* check the range of the group id */ errno = 0; gid = strtoul(f[2], NULL, 10); if (errno != 0) { warnx("%s: line %d: strtoul failed", gfn, n); } else if (gid > GID_MAX) { warnx("%s: line %d: group id is too large (%ju > %ju)", gfn, n, (uintmax_t)gid, (uintmax_t)GID_MAX); e = 1; } } /* check what broke the loop */ if (ferror(gf)) err(EX_IOERR, "%s: line %d", gfn, n); /* done */ fclose(gf); if (e == 0 && quiet == 0) printf("%s is fine\n", gfn); exit(e ? EX_DATAERR : EX_OK); } diff --git a/usr.sbin/kldxref/kldxref.8 b/usr.sbin/kldxref/kldxref.8 index 4a06e62e7feb..0b141bb18a1c 100644 --- a/usr.sbin/kldxref/kldxref.8 +++ b/usr.sbin/kldxref/kldxref.8 @@ -1,103 +1,103 @@ .\"- .\" Copyright (c) 2001 Boris Popov -.\" Copyright (c) 2001 Dag-Erling Coïdan Smørgrav +.\" Copyright (c) 2001 Dag-Erling Smørgrav .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd February 25, 2023 .Dt KLDXREF 8 .Os .Sh NAME .Nm kldxref .Nd generate hints for the kernel loader .Sh SYNOPSIS .Nm .Op Fl Rdv .Op Fl f Ar hintsfile .Ar path ... .Sh DESCRIPTION The .Nm utility is used to generate hint files which list modules, their version numbers, and the files that contain them. These hints are used by the kernel loader to determine where to find a particular KLD module. .Pp A separate hint file is generated for each directory listed on the command line that contains modules. If no hint records are generated for a particular directory, no hint file is created, and the preexisting hint file (if there was one in that directory) is removed. .Pp .Nm ignores files with at least two "."s in the filename, such as .Pa foo.ko.debug or .Pa bar.ko.pkgsave . Note that this means that modules cannot have names such as .Pa foo.bar.ko . This limitation however, has been lived practice since the beginning of FreeBSD's kernel modules. .Pp The following options are available: .Bl -tag -width indent .It Fl R Recurse into subdirectories. .It Fl d Do not generate a hint file, but print module metadata on standard output. .It Fl f Ar hintsfile Specify a different name for the hints files than .Pa linker.hints . .It Fl v Operate in verbose mode. .El .Sh EXAMPLES To build hint files for both standard and add-on modules: .Pp .Dl "kldxref /boot/kernel /boot/modules" .Pp To build hint files for all installed kernels: .Pp .Dl "kldxref -R /boot" .Sh SEE ALSO .Xr kld 4 , .Xr kldconfig 8 , .Xr kldload 8 , .Xr kldstat 8 , .Xr kldunload 8 .Sh HISTORY The .Nm utility first appeared in .Fx 5.0 . .Sh AUTHORS .An -nosplit The .Nm utility was implemented by .An Boris Popov Aq Mt bp@FreeBSD.org . This manual page was written by .An Boris Popov Aq Mt bp@FreeBSD.org and .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .