Remove remaining NetBSD references
This commit is contained in:
parent
ac723761ad
commit
1b0b27f8d1
2945
bsd_tree.txt
Normal file
2945
bsd_tree.txt
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +0,0 @@
|
||||||
PROG= cdprobe
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/fs
|
|
||||||
MAN= cdprobe.8
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
# Makefile for crontab.
|
|
||||||
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= crontab
|
|
||||||
SRCS= crontab.c tab.c misc.c
|
|
||||||
CPPFLAGS+= -I${.CURDIR} -I${NETBSDSRCDIR}/minix/commands/cron
|
|
||||||
BINMODE= 4755
|
|
||||||
|
|
||||||
.PATH: ${NETBSDSRCDIR}/minix/commands/cron
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +0,0 @@
|
||||||
FSCK= ${NETBSDSRCDIR}/sbin/fsck
|
|
||||||
PROG= fsck_mfs
|
|
||||||
SRCS= fsck.c
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/fs -I${FSCK}
|
|
||||||
BINDIR= /sbin
|
|
||||||
MAN= fsck.mfs.1
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
PROG= partition
|
|
||||||
MAN= partition.8
|
|
||||||
|
|
||||||
# We need this to find our partition.h while compiling natively
|
|
||||||
# on non-Minix.
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/include/arch/${MACHINE_ARCH}/include
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
PROG= writeisofs
|
|
||||||
MAN=
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/include/arch/${MACHINE_ARCH}/include
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
# Makefile for PCI support
|
|
||||||
PROG= pci
|
|
||||||
SRCS= main.c pci.c pci_table.c
|
|
||||||
|
|
||||||
# Directly taken from sys/dev/pci
|
|
||||||
SRCS+= dev_verbose.c pci_subr.c
|
|
||||||
|
|
||||||
.PATH.c: ${NETBSDSRCDIR}/sys/dev/pci
|
|
||||||
.PATH.c: ${NETBSDSRCDIR}/sys/dev
|
|
||||||
|
|
||||||
CPPFLAGS+= -I ${NETBSDSRCDIR}/sys
|
|
||||||
CPPFLAGS.pci_subr.c+= -D_PCI_SERVER
|
|
||||||
|
|
||||||
CWARNFLAGS.clang=-Wno-unused
|
|
||||||
|
|
||||||
DPADD+= ${LIBCHARDRIVER}
|
|
||||||
LDADD+= -lchardriver
|
|
||||||
|
|
||||||
DPADD+= ${LIBACPI}
|
|
||||||
LDADD+= -lacpi
|
|
||||||
|
|
||||||
WARNS= 3
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Makefile for the i2c eeprom commonly found on the BeagleBone (CAT24C256)
|
|
||||||
PROG= cat24c256
|
|
||||||
SRCS= cat24c256.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBBLOCKDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lblockdriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
# Makefile for the Ethernet drivers
|
|
||||||
PROG= ip1000
|
|
||||||
SRCS= ip1000.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBNETDRIVER} ${LIBSYS}
|
|
||||||
LDADD+= -lnetdriver -lsys
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Makefile for the Realtek RTL8139 ethernet driver (RTL8139)
|
|
||||||
PROG= rtl8139
|
|
||||||
SRCS= rtl8139.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBNETDRIVER} ${LIBSYS}
|
|
||||||
LDADD+= -lnetdriver -lsys
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
WARNS?= 5
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Makefile for the Realtek RTL8169 ethernet driver (RTL8169)
|
|
||||||
PROG= rtl8169
|
|
||||||
SRCS= rtl8169.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBNETDRIVER} ${LIBSYS}
|
|
||||||
LDADD+= -lnetdriver -lsys
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
WARNS?= 5
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
# Makefile for the VIA Technology 6105/6106S Ethernet driver (vt6105)
|
|
||||||
PROG= vt6105
|
|
||||||
SRCS= vt6105.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBNETDRIVER} ${LIBSYS}
|
|
||||||
LDADD+= -lnetdriver -lsys
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Makefile for the tps65217 Power Management IC found on the BeagleBones
|
|
||||||
PROG= tps65217
|
|
||||||
SRCS= tps65217.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBCLKCONF} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lclkconf -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Makefile for the tps65950 PMIC found on the BeagleBoard-xM.
|
|
||||||
PROG= tps65950
|
|
||||||
SRCS= tps65950.c tps65950.h rtc.c rtc.h
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Makefile for the bmp085 pressure and temp sensor found on the Weather Cape.
|
|
||||||
|
|
||||||
PROG= bmp085
|
|
||||||
SRCS= bmp085.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lchardriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Makefile for the sht21 humidity and temp sensor found on the Weather Cape.
|
|
||||||
|
|
||||||
PROG= sht21
|
|
||||||
SRCS= sht21.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lchardriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Makefile for the tsl2550 ambient light sensor found on the Weather Cape.
|
|
||||||
|
|
||||||
PROG= tsl2550
|
|
||||||
SRCS= tsl2550.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lchardriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# Makefile for memory driver (MEMORY)
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
USE_BITCODE:=no
|
|
||||||
|
|
||||||
PROG= memory
|
|
||||||
SRCS= memory.c imgrd.mfs
|
|
||||||
OBJS= ${SRCS:N*.h:R:S/$/.o/g}
|
|
||||||
MKBUILDEXT2RD?= no
|
|
||||||
|
|
||||||
RAMDISK_PATH= ${NETBSDSRCDIR}/minix/drivers/storage/ramdisk
|
|
||||||
DPADD+= ${LIBBLOCKDRIVER} ${LIBCHARDRIVER}
|
|
||||||
LDADD+= -lblockdriver -lchardriver
|
|
||||||
|
|
||||||
CPPFLAGS.memory.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
imgrd.d: touch-genfiles
|
|
||||||
touch-genfiles:
|
|
||||||
[ -e ../ramdisk/image ] || touch -t 197001020000.00 ../ramdisk/image
|
|
||||||
|
|
||||||
|
|
||||||
.SUFFIXES: .mfs .c .o
|
|
||||||
|
|
||||||
.mfs.o:
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
${OBJCOPY} -Ibinary -B${MACHINE_CPU} -O${MACHINE_GNU_PLATFORM} $< $@
|
|
||||||
|
|
||||||
CLEANFILES+= ../ramdisk/image
|
|
||||||
# BJG - don't invoke parallel Makes
|
|
||||||
#../ramdisk/image: .PHONY
|
|
||||||
# ${MAKE} -C ${RAMDISK_PATH} image
|
|
||||||
|
|
||||||
CLEANFILES+= imgrd.mfs
|
|
||||||
imgrd.mfs: ../ramdisk/image
|
|
||||||
${HOST_LN} -fs ../ramdisk/image $@
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
# Makefile for ramdisk image
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
USE_BITCODE:=no
|
|
||||||
|
|
||||||
install:
|
|
||||||
|
|
||||||
all:
|
|
||||||
|
|
||||||
# Add a few defines we are going to use during the image
|
|
||||||
# creation to determine what features and binaries to include
|
|
||||||
# in the final image
|
|
||||||
# ACPI do we have/include the acpi binary
|
|
||||||
# RAMDISK_SMALL is the script called with MKSMALL=yes
|
|
||||||
# DYNAMIC does the ramdisk contain dynamic binaries?
|
|
||||||
RAMDISK_INC_ACPI= 0
|
|
||||||
RAMDISK_SMALL= 0
|
|
||||||
RAMDISK_DYNAMIC= 0
|
|
||||||
|
|
||||||
RAMDISK_DEFINES= \
|
|
||||||
-DRAMDISK_INC_ACPI=${RAMDISK_INC_ACPI} \
|
|
||||||
-DRAMDISK_SMALL=${RAMDISK_SMALL} \
|
|
||||||
-DRAMDISK_DYNAMIC=${RAMDISK_DYNAMIC}
|
|
||||||
|
|
||||||
# The name of the proto file to use
|
|
||||||
PROTO= proto
|
|
||||||
|
|
||||||
# Common to all architectures
|
|
||||||
ETC= system.conf group
|
|
||||||
EXTRA= rc
|
|
||||||
PROTO_FILES= proto.common.etc
|
|
||||||
PROTO_FILES+= proto.common.dynamic
|
|
||||||
PROGRAMS= # defined
|
|
||||||
PROGRAMS+= fsck_mfs
|
|
||||||
dir.fsck_mfs:= minix/commands/fsck.mfs
|
|
||||||
PROGRAMS+= grep
|
|
||||||
dir.grep:= minix/usr.bin/grep
|
|
||||||
PROGRAMS+= input
|
|
||||||
dir.input:= minix/servers/input
|
|
||||||
PROGRAMS+= loadramdisk
|
|
||||||
dir.loadramdisk:= minix/commands/loadramdisk
|
|
||||||
PROGRAMS+= mfs
|
|
||||||
dir.mfs:= minix/fs/mfs
|
|
||||||
PROGRAMS+= mount
|
|
||||||
dir.mount:= minix/commands/mount
|
|
||||||
PROGRAMS+= procfs
|
|
||||||
dir.procfs:= minix/fs/procfs
|
|
||||||
PROGRAMS+= minix-service
|
|
||||||
dir.minix-service:= minix/commands/minix-service
|
|
||||||
PROGRAMS+= sh
|
|
||||||
dir.sh:= bin/sh
|
|
||||||
PROGRAMS+= sysenv
|
|
||||||
dir.sysenv:= minix/commands/sysenv
|
|
||||||
PROGRAMS+= umount
|
|
||||||
dir.umount:= minix/commands/umount
|
|
||||||
|
|
||||||
.if ${MKSMALL} != "yes"
|
|
||||||
RAMDISK_SMALL= 1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "i386"
|
|
||||||
ETC+= rs.single
|
|
||||||
PROGRAMS+= at_wini
|
|
||||||
dir.at_wini:= minix/drivers/storage/at_wini
|
|
||||||
PROGRAMS+= floppy
|
|
||||||
dir.floppy:= minix/drivers/storage/floppy
|
|
||||||
PROGRAMS+= pci
|
|
||||||
dir.pci:= minix/drivers/bus/pci
|
|
||||||
PROGRAMS+= pckbd
|
|
||||||
dir.pckbd:= minix/drivers/hid/pckbd
|
|
||||||
PROGRAMS+= cdprobe
|
|
||||||
dir.cdprobe:= minix/commands/cdprobe
|
|
||||||
PROGRAMS+= pwd_mkdb
|
|
||||||
dir.pwd_mkdb:= usr.sbin/pwd_mkdb
|
|
||||||
PROGRAMS+= isofs
|
|
||||||
dir.isofs:= minix/fs/isofs
|
|
||||||
|
|
||||||
.if ${MKSMALL} != "yes"
|
|
||||||
PROGRAMS+= ahci
|
|
||||||
dir.ahci:= minix/drivers/storage/ahci
|
|
||||||
PROGRAMS+= virtio_blk
|
|
||||||
dir.virtio_blk:= minix/drivers/storage/virtio_blk
|
|
||||||
PROGRAMS+= ext2
|
|
||||||
dir.ext2:= minix/fs/ext2
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${MKACPI} != "no"
|
|
||||||
RAMDISK_INC_ACPI= 1
|
|
||||||
PROGRAMS+= acpi
|
|
||||||
dir.acpi:= minix/drivers/power/acpi
|
|
||||||
.endif
|
|
||||||
.endif # ${MACHINE_ARCH} == "i386"
|
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm"
|
|
||||||
PROGRAMS+= mmc
|
|
||||||
dir.mmc:= minix/drivers/storage/mmc
|
|
||||||
.endif # ${MACHINE_ARCH} == "earm"
|
|
||||||
|
|
||||||
.if ${LDSTATIC} == "-dynamic"
|
|
||||||
RAMDISK_DYNAMIC= 1
|
|
||||||
PROGRAMS+= ld.elf_so
|
|
||||||
PROG_LIBS+= libc
|
|
||||||
PROG_MINIXLIBS+= libterminfo
|
|
||||||
.endif
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/servers
|
|
||||||
|
|
||||||
# LSC We have to take care of not erasing the source file, so never add EXTRA
|
|
||||||
# to CLEANFILES
|
|
||||||
CLEANFILES += ${PROGRAMS} ${SCRIPTS} ${ETC} image image.c
|
|
||||||
CLEANFILES += proto.gen proto.dev proto.dev.mtree
|
|
||||||
CLEANFILES += ${PROG_LIBEXEC}
|
|
||||||
CLEANFILES += ${.OBJDIR}/etc/*
|
|
||||||
|
|
||||||
#############################################################
|
|
||||||
# LSC Below this point the rules should not be modified
|
|
||||||
#############################################################
|
|
||||||
|
|
||||||
# Tool to bootstrap the password db
|
|
||||||
TOOL_PWD_MKDB?= ${NETBSDSRCDIR}/usr.sbin/pwd_mkdb/pwd_mkdb
|
|
||||||
|
|
||||||
# Remove "drivers/storage/ramdisk" component from path
|
|
||||||
PROGROOT:= ${.OBJDIR:S,/minix/drivers/storage/ramdisk,,}
|
|
||||||
|
|
||||||
# Generate dependencies rules for config files
|
|
||||||
.for etc in ${ETC}
|
|
||||||
etc/${etc}: ${NETBSDSRCDIR}/etc/${etc}
|
|
||||||
mkdir -p ${.OBJDIR}/etc
|
|
||||||
${INSTALL} $> $@
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
# LSC Force the regeneration of the proto file as it seems sometimes
|
|
||||||
# they are not copied over as needed.
|
|
||||||
# LSC ramdisk rc file != /etc/rc
|
|
||||||
# BJG if ${.CURDIR} == ${.OBJDIR}, we are building in-tree and install
|
|
||||||
# shouldn't try to install the originals over the originals. Otherwise
|
|
||||||
# we are building out-of-tree and the contents should be copied
|
|
||||||
# unconditionally in case the contents have changed after the first copy.
|
|
||||||
.if ${.CURDIR} != ${.OBJDIR}
|
|
||||||
.for file in ${EXTRA} ${PROTO} ${PROTO_FILES}
|
|
||||||
CLEANFILES += ${.OBJDIR}/${file}
|
|
||||||
${file}: ${NETBSDSRCDIR}/minix/drivers/storage/ramdisk/${file} .PHONY
|
|
||||||
${INSTALL} $> $@
|
|
||||||
.endfor
|
|
||||||
.endif
|
|
||||||
|
|
||||||
# Generate dependencies rules for dynamic libraries, if needed
|
|
||||||
.for lib in ${PROG_LIBS}
|
|
||||||
PROGRAMS+= ${lib}.so.0
|
|
||||||
CLEANFILES += ${lib}.so.0
|
|
||||||
${lib}.so.0:${PROGROOT}/lib/${lib}/${lib}.so.0
|
|
||||||
${INSTALL} $> $@
|
|
||||||
|
|
||||||
# BJG - Don't invoke parallel Makes
|
|
||||||
#${PROGROOT}/lib/${lib}/${lib}.so.0:
|
|
||||||
# ${MAKE} -C ${NETBSDSRCDIR}/lib/${lib} all
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for lib in ${PROG_MINIXLIBS}
|
|
||||||
PROGRAMS+= ${lib}.so.0
|
|
||||||
CLEANFILES += ${lib}.so.0
|
|
||||||
${lib}.so.0:${PROGROOT}/minix/lib/${lib}/${lib}.so.0
|
|
||||||
${INSTALL} $> $@
|
|
||||||
|
|
||||||
# BJG - Don't invoke parallel Makes
|
|
||||||
#${PROGROOT}/minix/lib/${lib}/${lib}.so.0:
|
|
||||||
# ${MAKE} -C ${NETBSDSRCDIR}/minix/lib/${lib} all
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
# Generate dependencies rules for binaries
|
|
||||||
.for prog in ${PROGRAMS}
|
|
||||||
${prog}: ${PROGROOT}/${dir.${prog}}/${prog}
|
|
||||||
${INSTALL} $> $@
|
|
||||||
|
|
||||||
# BJG - don't invoke parallel Makes
|
|
||||||
#${PROGROOT}/${dir.${prog}}/${prog}:
|
|
||||||
# ${MAKE} -C ${NETBSDSRCDIR}/${dir.${prog}} all
|
|
||||||
.endfor # prog
|
|
||||||
|
|
||||||
realall image: proto.gen ${ETC:C/^/etc\//} ${EXTRA} \
|
|
||||||
etc/master.passwd etc/pwd.db etc/spwd.db etc/passwd etc/group
|
|
||||||
${_MKMSG_CREATE} "Generating ramdisk image"
|
|
||||||
${TOOL_MKFSMFS} image proto.gen || { rm -f image; false; }
|
|
||||||
|
|
||||||
etc/pwd.db etc/spwd.db etc/passwd: etc/master.passwd
|
|
||||||
|
|
||||||
etc/master.passwd: ${NETBSDSRCDIR}/etc/master.passwd
|
|
||||||
mkdir -p ${.OBJDIR}/etc
|
|
||||||
${INSTALL} $> $@
|
|
||||||
${TOOL_PWD_MKDB} -V 0 -p -d . etc/master.passwd
|
|
||||||
|
|
||||||
proto.dev.mtree:
|
|
||||||
@echo ". type=dir uname=root gname=wheel mode=0755" \
|
|
||||||
>${.TARGET}.tmp && \
|
|
||||||
echo "./dev type=dir uname=root gname=wheel mode=0755" \
|
|
||||||
>>${.TARGET}.tmp && \
|
|
||||||
${NETBSDSRCDIR}/minix/commands/MAKEDEV/MAKEDEV.sh -m -r \
|
|
||||||
>> ${.TARGET}.tmp && mv ${.TARGET}.tmp ${.TARGET}
|
|
||||||
|
|
||||||
# We have to remove the two first entries of the generated proto file, as
|
|
||||||
# well as the two last ones (closing $).
|
|
||||||
# Do not forget to double $ so that make doesn't try to expand it.
|
|
||||||
proto.dev: proto.dev.mtree etc/pwd.db etc/spwd.db etc/passwd etc/group
|
|
||||||
${TOOL_MTREE} -f ${.TARGET}.mtree -N ${.OBJDIR}/etc -C -K device | \
|
|
||||||
${TOOL_TOPROTO} | ${TOOL_SED} -e '1,4d' | \
|
|
||||||
${TOOL_SED} -e '$$d' |${TOOL_SED} -e '$$d' > ${.TARGET}.tmp
|
|
||||||
grep console ${.TARGET}.tmp # sanity check; is there a console entry?
|
|
||||||
mv ${.TARGET}.tmp ${.TARGET}
|
|
||||||
|
|
||||||
proto.gen: ${PROTO} ${PROTO_FILES} proto.dev ${PROGRAMS}
|
|
||||||
${STRIP} ${PROGRAMS}
|
|
||||||
# We are using the c preprocessor to generate proto.gen
|
|
||||||
# used in the mkfs tool.
|
|
||||||
${TOOL_CAT} ${PROTO} | ${CC} \
|
|
||||||
${RAMDISK_DEFINES} \
|
|
||||||
-E - | grep -v "^$$" | grep -v "#" >${.TARGET}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Makefile for random driver (RANDOM)
|
|
||||||
PROG= random
|
|
||||||
SRCS= main.c random.c rijndael_api.c rijndael_alg.c
|
|
||||||
|
|
||||||
FILES=${PROG}.conf
|
|
||||||
FILESNAME=${PROG}
|
|
||||||
FILESDIR= /etc/system.conf.d
|
|
||||||
|
|
||||||
.PATH: ${.CURDIR}/aes
|
|
||||||
|
|
||||||
DPADD+= ${LIBCHARDRIVER} ${LIBSYS}
|
|
||||||
LDADD+= -lchardriver -lsys
|
|
||||||
|
|
||||||
CPPFLAGS.random.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Makefile for terminal driver (TTY)
|
|
||||||
PROG= tty
|
|
||||||
|
|
||||||
.include "arch/${MACHINE_ARCH}/Makefile.inc"
|
|
||||||
|
|
||||||
SRCS += tty.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -lchardriver -lsys -ltimers
|
|
||||||
|
|
||||||
SUBDIR= keymaps
|
|
||||||
|
|
||||||
# Needs kernel/const.h, etc
|
|
||||||
CPPFLAGS+= -I${.CURDIR} -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
.include <bsd.subdir.mk>
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# Generate binary keymaps.
|
|
||||||
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
FILES= dvorak.map french.map german.map hungarian.map italian.map \
|
|
||||||
japanese.map latin-america.map norwegian.map polish.map \
|
|
||||||
russian-cp866.map russian.map scandinavian.map \
|
|
||||||
spanish.map uk.map us-std.map us-swap.map russian-cp1251.map \
|
|
||||||
ukraine-koi8-u.map portuguese.map abnt2.map colemak.map
|
|
||||||
|
|
||||||
FILESDIR= /usr/lib/keymaps
|
|
||||||
|
|
||||||
.SUFFIXES: .src .map
|
|
||||||
|
|
||||||
HOST_CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.src.map:
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
${HOST_CC} ${HOST_CFLAGS} ${HOST_CPPFLAGS} ${HOST_LDFLAGS} -DKEYSRC=\"$<\" -o ${.OBJDIR}/${.TARGET}_genmap ${.CURDIR}/genmap.c
|
|
||||||
${.OBJDIR}/${.TARGET}_genmap > ${.OBJDIR}/${.TARGET}
|
|
||||||
rm -f ${.OBJDIR}/${.TARGET}_genmap
|
|
||||||
|
|
||||||
realall: ${FILES}
|
|
||||||
|
|
||||||
CLEANFILES+= ${FILES}
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Makefile for the tda19988 HDMI framer found on the BeagleBone Black.
|
|
||||||
PROG= tda19988
|
|
||||||
SRCS= tda19988.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBI2CDRIVER} ${LIBBLOCKDRIVER} ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -li2cdriver -lblockdriver -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
# Makefile for ProcFS server
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= procfs
|
|
||||||
SRCS= buf.c cpuinfo.c main.c pid.c root.c service.c tree.c util.c
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/fs
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/servers
|
|
||||||
|
|
||||||
DPADD+= ${LIBVTREEFS} ${LIBFSDRIVER}
|
|
||||||
LDADD+= -lvtreefs -lfsdriver
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#ifndef _MINIX_DRIVER_H
|
|
||||||
#define _MINIX_DRIVER_H
|
|
||||||
|
|
||||||
/* Types and constants shared between block and character drivers. */
|
|
||||||
|
|
||||||
#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
|
|
||||||
#define _NETBSD_SOURCE 1 /* tell headers to include MINIX stuff */
|
|
||||||
#define _SYSTEM 1 /* get negative error number in <errno.h> */
|
|
||||||
|
|
||||||
/* The following are so basic, all the *.c files get them automatically. */
|
|
||||||
#include <minix/config.h> /* MUST be first */
|
|
||||||
#include <minix/type.h>
|
|
||||||
#include <minix/ipc.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <minix/const.h>
|
|
||||||
#include <minix/syslib.h>
|
|
||||||
#include <minix/sysutil.h>
|
|
||||||
#include <minix/endpoint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <minix/u64.h>
|
|
||||||
#include <minix/partition.h>
|
|
||||||
|
|
||||||
/* Base and size of a partition in bytes. */
|
|
||||||
struct device {
|
|
||||||
u64_t dv_base;
|
|
||||||
u64_t dv_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Generic receive function for all drivers. */
|
|
||||||
#ifndef driver_receive
|
|
||||||
#define driver_receive sef_receive_status
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Maximum supported number of concurrently opened minor devices. */
|
|
||||||
#define MAX_NR_OPEN_DEVICES 256
|
|
||||||
|
|
||||||
#endif /* _MINIX_DRIVER_H */
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/* This is the master header for all device drivers. It includes some other
|
|
||||||
* files and defines the principal constants.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INC_DRIVERS_H
|
|
||||||
#define _INC_DRIVERS_H
|
|
||||||
|
|
||||||
#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
|
|
||||||
#define _NETBSD_SOURCE 1 /* tell headers to include MINIX stuff */
|
|
||||||
#define _SYSTEM 1 /* get negative error number in <errno.h> */
|
|
||||||
|
|
||||||
/* The following are so basic, all the *.c files get them automatically. */
|
|
||||||
#include <minix/config.h> /* MUST be first */
|
|
||||||
#include <minix/bitmap.h>
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <minix/const.h>
|
|
||||||
#include <minix/devio.h>
|
|
||||||
#include <minix/dmap.h>
|
|
||||||
#include <minix/spin.h>
|
|
||||||
#include <minix/syslib.h>
|
|
||||||
#include <minix/sysutil.h>
|
|
||||||
#include <minix/timers.h>
|
|
||||||
#include <minix/type.h>
|
|
||||||
#include <minix/ds.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <machine/interrupt.h> /* IRQ vectors and miscellaneous ports */
|
|
||||||
#if defined(__i386__)
|
|
||||||
#include <machine/bios.h> /* BIOS index numbers */
|
|
||||||
#include <machine/ports.h> /* Well-known ports */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <lib.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
# Makefile for kernel
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= kernel
|
|
||||||
BINDIR= /usr/sbin
|
|
||||||
MAN=
|
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm" && ${MKLLVM:Uno} == "yes"
|
|
||||||
# BJG - problems with optimisation of the kernel by llvm
|
|
||||||
DBG=-O0
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.include "arch/${MACHINE_ARCH}/Makefile.inc"
|
|
||||||
|
|
||||||
SRCS+= clock.c cpulocals.c interrupt.c main.c proc.c system.c \
|
|
||||||
table.c utility.c usermapped_data.c
|
|
||||||
|
|
||||||
LDADD+= -ltimers -lsys -lexec
|
|
||||||
|
|
||||||
LINKERSCRIPT= ${.CURDIR}/arch/${MACHINE_ARCH}/kernel.lds
|
|
||||||
|
|
||||||
.if ${HAVE_GOLD:U} != ""
|
|
||||||
CFLAGS+= -fno-common
|
|
||||||
.endif
|
|
||||||
LDFLAGS+= -T ${LINKERSCRIPT}
|
|
||||||
LDFLAGS+= -nostdlib -L${DESTDIR}/usr/lib
|
|
||||||
CFLAGS += -fno-stack-protector
|
|
||||||
|
|
||||||
CPPFLAGS+= -D__kernel__
|
|
||||||
|
|
||||||
# kernel headers are always called through kernel/*.h
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
# kernel headers are always called through kernel/*.h, this
|
|
||||||
# time for generated headers, during cross compilation
|
|
||||||
CPPFLAGS+= -I${.OBJDIR}/..
|
|
||||||
|
|
||||||
# Machine-dependent headers, order is important!
|
|
||||||
CPPFLAGS+= -I${.CURDIR}/arch/${MACHINE_ARCH}
|
|
||||||
CPPFLAGS+= -I${.CURDIR}/arch/${MACHINE_ARCH}/include
|
|
||||||
CPPFLAGS+= -I${.CURDIR}/arch/${MACHINE_ARCH}/bsp/include
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/include/arch/${MACHINE_ARCH}/include
|
|
||||||
|
|
||||||
.include "system/Makefile.inc"
|
|
||||||
|
|
||||||
.if ${MKPAE:Uno} != "no"
|
|
||||||
CPPFLAGS+= -DPAE=1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.ifdef CONFIG_SMP
|
|
||||||
SRCS+= smp.c
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_WATCHDOG} != "no"
|
|
||||||
SRCS+= watchdog.c
|
|
||||||
CPPFLAGS+= -DUSE_WATCHDOG=1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
# Extra debugging routines
|
|
||||||
.if ${USE_SYSDEBUG} != "no"
|
|
||||||
SRCS+= debug.c
|
|
||||||
CPPFLAGS+= -DUSE_SYSDEBUG=1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
# These come last, so the profiling buffer is at the end of the data segment
|
|
||||||
SRCS+= profile.c do_sprofile.c
|
|
||||||
|
|
||||||
.if ${USE_LIVEUPDATE} != "no"
|
|
||||||
CPPFLAGS+= -DUSE_UPDATE=1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
CLEANFILES+=extracted-errno.h extracted-mfield.h extracted-mtype.h procoffsets.h
|
|
||||||
|
|
||||||
debug.o debug.d: extracted-errno.h extracted-mfield.h extracted-mtype.h
|
|
||||||
|
|
||||||
extracted-errno.h: extract-errno.sh ../../include/errno.h
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
cd ${.CURDIR} ; ${HOST_SH} extract-errno.sh > ${.OBJDIR}/extracted-errno.h
|
|
||||||
|
|
||||||
extracted-mfield.h: extract-mfield.sh ../lib/libc/sys/*.c ../lib/libsys/*.c
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
cd ${.CURDIR} ; ${HOST_SH} extract-mfield.sh > ${.OBJDIR}/extracted-mfield.h
|
|
||||||
|
|
||||||
extracted-mtype.h: extract-mtype.sh ../include/minix/com.h
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
cd ${.CURDIR} ; ${HOST_SH} extract-mtype.sh > ${.OBJDIR}/extracted-mtype.h
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
# dcvmoole: this is a copy of the "${_P}: ${_P}.bcl.o" block from bsd.prog.mk,
|
|
||||||
# with two changes: 1) ${OBJS} is added so as to link in objects that have not
|
|
||||||
# been compiled with bitcode, and 2) we are directly loading the gold plugin
|
|
||||||
# rather than through ${BITCODE_LD_FLAGS_2ND.kernel}, because LLVMgold will
|
|
||||||
# not load libLTO when no LTO can be performed (due to the non- bitcode
|
|
||||||
# objects), causing it to fail on unrecognized -disable-opt/-disable-inlining
|
|
||||||
# options. At least I think that's what's going on? I'm no expert here..
|
|
||||||
kernel: kernel.bcl.o
|
|
||||||
${_MKTARGET_LINK}
|
|
||||||
${_CCLINK.kernel} \
|
|
||||||
${_LDFLAGS.kernel} \
|
|
||||||
-L${DESTDIR}/usr/lib \
|
|
||||||
${_LDSTATIC.kernel} -o ${.TARGET} \
|
|
||||||
${.TARGET}.bcl.o ${OBJS} ${_PROGLDOPTS} ${_LDADD.kernel} \
|
|
||||||
-Wl,-plugin=${GOLD_PLUGIN} \
|
|
||||||
-Wl,--allow-multiple-definition
|
|
||||||
.endif
|
|
||||||
|
|
||||||
# Disable magic and ASR passes for the kernel.
|
|
||||||
USE_MAGIC=no
|
|
||||||
|
|
||||||
# Disable coverage profiling for the kernel, at least for now.
|
|
||||||
MKCOVERAGE=no
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
# Makefile for arch-dependent kernel code
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
HERE=${.CURDIR}/arch/${MACHINE_ARCH}
|
|
||||||
.PATH: ${HERE}
|
|
||||||
|
|
||||||
# objects we want unpaged from -lc
|
|
||||||
MINLIB_OBJS_UNPAGED= get_bp.o
|
|
||||||
get_bp.o: ${NETBSDSRCDIR}/minix/lib/libc/arch/arm/get_bp.S
|
|
||||||
|
|
||||||
# objects we want unpaged from -lsys
|
|
||||||
SYS_OBJS_UNPAGED=assert.o stacktrace.o
|
|
||||||
assert.o: ${NETBSDSRCDIR}/minix/lib/libsys/assert.c
|
|
||||||
stacktrace.o: ${NETBSDSRCDIR}/minix/lib/libsys/stacktrace.c
|
|
||||||
|
|
||||||
# objects we want unpaged from -lminc
|
|
||||||
MINC_OBJS_UNPAGED= atoi.o \
|
|
||||||
printf.o subr_prf.o \
|
|
||||||
strcmp.o strcpy.o strlen.o strncmp.o \
|
|
||||||
memcpy.o memmove.o memset.o
|
|
||||||
MINC_OBJS_UNPAGED+= divmodsi4.o divsi3.o udivsi3.o umodsi3.o \
|
|
||||||
umoddi3.o udivmoddi4.o __aeabi_idiv0.o aeabi_idivmod.o aeabi_uidivmod.o \
|
|
||||||
udivmodsi4.o aeabi_uldivmod.o
|
|
||||||
atoi.o: ${NETBSDSRCDIR}/minix/lib/libminc/atoi.c
|
|
||||||
printf.o: ${NETBSDSRCDIR}/sys/lib/libsa/printf.c
|
|
||||||
subr_prf.o: ${NETBSDSRCDIR}/sys/lib/libsa/subr_prf.c
|
|
||||||
memcpy.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/memcpy.S
|
|
||||||
memmove.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/memmove.S
|
|
||||||
memset.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/memset.S
|
|
||||||
strlen.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/strlen.S
|
|
||||||
strcpy.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/strcpy.S
|
|
||||||
strcmp.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/string/strcmp.S
|
|
||||||
__aeabi_idiv0.o: ${NETBSDSRCDIR}/common/lib/libc/arch/arm/gen/__aeabi_idiv0.c
|
|
||||||
CPPFLAGS.__aeabi_idiv0.c+= -D_STANDALONE -I${NETBSDSRCDIR}/sys
|
|
||||||
|
|
||||||
divsi3.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/divsi3.c
|
|
||||||
udivsi3.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/udivsi3.c
|
|
||||||
umodsi3.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/umodsi3.c
|
|
||||||
umoddi3.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/umoddi3.c
|
|
||||||
udivmoddi4.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/udivmoddi4.c
|
|
||||||
divmodsi4.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/divmodsi4.S
|
|
||||||
udivmodsi4.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/udivmodsi4.S
|
|
||||||
aeabi_idivmod.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/aeabi_idivmod.S
|
|
||||||
aeabi_uidivmod.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/aeabi_uidivmod.S
|
|
||||||
aeabi_uldivmod.o: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/aeabi_uldivmod.S
|
|
||||||
|
|
||||||
# the following is required by pre_init.c
|
|
||||||
strncmp.o: ${NETBSDSRCDIR}/common/lib/libc/string/strncmp.c
|
|
||||||
|
|
||||||
# LSC: putchar and kputc have the same signature. A bit hackish.
|
|
||||||
CPPFLAGS.subr_prf.c+= -Dputchar=kputc
|
|
||||||
|
|
||||||
# Activate optional support, may be deactivated.
|
|
||||||
CPPFLAGS.subr_prf.c+= -DLIBSA_PRINTF_LONGLONG_SUPPORT -DLIBSA_PRINTF_WIDTH_SUPPORT
|
|
||||||
|
|
||||||
.include "bsp/ti/Makefile.inc"
|
|
||||||
|
|
||||||
# some object files we give a symbol prefix (or namespace) of __k_unpaged_
|
|
||||||
# that must live in their own unique namespace.
|
|
||||||
#
|
|
||||||
.for unpaged_obj in head.o pre_init.o direct_tty_utils.o \
|
|
||||||
pg_utils.o klib.o utility.o arch_reset.o \
|
|
||||||
${MINLIB_OBJS_UNPAGED} ${MINC_OBJS_UNPAGED} ${SYS_OBJS_UNPAGED} ${BSP_OBJS_UNPAGED}
|
|
||||||
unpaged_${unpaged_obj}: ${unpaged_obj}
|
|
||||||
${OBJCOPY} --prefix-symbols=__k_unpaged_ ${.OBJDIR}/${unpaged_obj} $@
|
|
||||||
UNPAGED_OBJS += unpaged_${unpaged_obj}
|
|
||||||
ORIG_UNPAGED_OBJS += ${unpaged_obj}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
|
|
||||||
CLEANFILES+= ${ORIG_UNPAGED_OBJS}
|
|
||||||
|
|
||||||
SRCS+= mpx.S arch_clock.c arch_do_vmctl.c arch_system.c do_padconf.c \
|
|
||||||
exception.c hw_intr.c klib.S memory.c \
|
|
||||||
protect.c direct_tty_utils.c arch_reset.c \
|
|
||||||
pg_utils.c phys_copy.S phys_memset.S exc.S
|
|
||||||
|
|
||||||
OBJS.kernel+= ${UNPAGED_OBJS}
|
|
||||||
|
|
||||||
klib.o mpx.o head.o: procoffsets.h
|
|
||||||
|
|
||||||
SRCS+= procoffsets.h
|
|
||||||
|
|
||||||
PROCOFFSETSCF=procoffsets.cf
|
|
||||||
|
|
||||||
.PATH: ${NETBSDSRCDIR}/minix/include/arch/${MACHINE_ARCH}/include
|
|
||||||
|
|
||||||
procoffsets.h: ${PROCOFFSETSCF} kernel.h proc.h stackframe.h archtypes.h
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
${TOOL_CAT} ${HERE}/${PROCOFFSETSCF} | \
|
|
||||||
${TOOL_GENASSYM} -- ${CC} ${CFLAGS:N-Wa,*} \
|
|
||||||
${CPPFLAGS} ${PROF} ${${USE_BITCODE:Uno} == "yes":? -fno-lto:} \
|
|
||||||
${GENASSYM_CPPFLAGS} >$@.tmp && \
|
|
||||||
mv -f $@.tmp $@
|
|
||||||
|
|
||||||
sconst.h: procoffsets.h
|
|
||||||
apic_asm.o head.o klib.o mpx.o: sconst.h
|
|
||||||
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
# Makefile for arch-dependent kernel code
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
HERE=${.CURDIR}/arch/${MACHINE_ARCH}
|
|
||||||
.PATH: ${HERE}
|
|
||||||
|
|
||||||
# objects we want unpaged from -lc
|
|
||||||
MINLIB_OBJS_UNPAGED= _cpufeature.o _cpuid.o get_bp.o
|
|
||||||
_cpufeature.o: ${NETBSDSRCDIR}/minix/lib/libc/arch/${MACHINE_ARCH}/_cpufeature.c
|
|
||||||
_cpuid.o: ${NETBSDSRCDIR}/minix/lib/libc/arch/${MACHINE_ARCH}/_cpuid.S
|
|
||||||
get_bp.o: ${NETBSDSRCDIR}/minix/lib/libc/arch/${MACHINE_ARCH}/get_bp.S
|
|
||||||
|
|
||||||
# objects we want unpaged from -lsys
|
|
||||||
SYS_OBJS_UNPAGED=assert.o stacktrace.o
|
|
||||||
assert.o: ${NETBSDSRCDIR}/minix/lib/libsys/assert.c
|
|
||||||
stacktrace.o: ${NETBSDSRCDIR}/minix/lib/libsys/stacktrace.c
|
|
||||||
|
|
||||||
# objects we want unpaged from -lminc
|
|
||||||
MINC_OBJS_UNPAGED= atoi.o \
|
|
||||||
printf.o subr_prf.o \
|
|
||||||
strcmp.o strcpy.o strlen.o strncmp.o \
|
|
||||||
memcpy.o memmove.o memset.o \
|
|
||||||
udivdi3.o umoddi3.o qdivrem.o
|
|
||||||
atoi.o: ${NETBSDSRCDIR}/minix/lib/libminc/atoi.c
|
|
||||||
printf.o: ${NETBSDSRCDIR}/sys/lib/libsa/printf.c
|
|
||||||
subr_prf.o: ${NETBSDSRCDIR}/sys/lib/libsa/subr_prf.c
|
|
||||||
memcpy.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/memcpy.S
|
|
||||||
memmove.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/memmove.S
|
|
||||||
memset.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/memset.S
|
|
||||||
strlen.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/strlen.S
|
|
||||||
strcpy.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/strcpy.S
|
|
||||||
strcmp.o: ${NETBSDSRCDIR}/common/lib/libc/arch/i386/string/strcmp.S
|
|
||||||
|
|
||||||
# the following is required by pre_init.c
|
|
||||||
strncmp.o: ${NETBSDSRCDIR}/common/lib/libc/string/strncmp.c
|
|
||||||
|
|
||||||
# these are required by kprintn.o:
|
|
||||||
udivdi3.o: ${NETBSDSRCDIR}/common/lib/libc/quad/udivdi3.c
|
|
||||||
umoddi3.o: ${NETBSDSRCDIR}/common/lib/libc/quad/umoddi3.c
|
|
||||||
qdivrem.o: ${NETBSDSRCDIR}/common/lib/libc/quad/qdivrem.c
|
|
||||||
|
|
||||||
# LSC: putchar and kputc have the same signature. A bit hackish.
|
|
||||||
CPPFLAGS.subr_prf.c+= -Dputchar=kputc
|
|
||||||
|
|
||||||
# Activate optional support, may be deactivated.
|
|
||||||
CPPFLAGS.subr_prf.c+= -DLIBSA_PRINTF_LONGLONG_SUPPORT -DLIBSA_PRINTF_WIDTH_SUPPORT
|
|
||||||
|
|
||||||
# some object files we give a symbol prefix (or namespace) of __k_unpaged_
|
|
||||||
# that must live in their own unique namespace.
|
|
||||||
#
|
|
||||||
.for unpaged_obj in head.o pre_init.o direct_tty_utils.o \
|
|
||||||
pg_utils.o klib.o utility.o arch_reset.o \
|
|
||||||
io_inb.o io_outb.o \
|
|
||||||
${MINLIB_OBJS_UNPAGED} ${MINC_OBJS_UNPAGED} ${SYS_OBJS_UNPAGED}
|
|
||||||
|
|
||||||
CLEANFILES+=${unpaged_obj}.bin
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
unpaged_${unpaged_obj}: ${unpaged_obj}
|
|
||||||
if file -b ${.OBJDIR}/${unpaged_obj} | grep -q '^LLVM'; then \
|
|
||||||
${LLC} -O1 -march=x86 -mcpu=i586 -filetype=obj -o ${.OBJDIR}/${unpaged_obj}.bin ${.OBJDIR}/${unpaged_obj}; \
|
|
||||||
else \
|
|
||||||
cp ${.OBJDIR}/${unpaged_obj} ${.OBJDIR}/${unpaged_obj}.bin; \
|
|
||||||
fi
|
|
||||||
${OBJCOPY} --prefix-symbols=__k_unpaged_ ${.OBJDIR}/${unpaged_obj}.bin $@
|
|
||||||
.else
|
|
||||||
unpaged_${unpaged_obj}: ${unpaged_obj}
|
|
||||||
${OBJCOPY} --prefix-symbols=__k_unpaged_ ${.OBJDIR}/${unpaged_obj} $@
|
|
||||||
.endif
|
|
||||||
UNPAGED_OBJS += unpaged_${unpaged_obj}
|
|
||||||
ORIG_UNPAGED_OBJS += ${unpaged_obj}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CLEANFILES+= ${ORIG_UNPAGED_OBJS}
|
|
||||||
|
|
||||||
SRCS+= mpx.S arch_clock.c arch_do_vmctl.c arch_system.c \
|
|
||||||
klib.S memory.c protect.c direct_tty_utils.c arch_reset.c pg_utils.c
|
|
||||||
|
|
||||||
SRCS+= do_iopenable.c do_readbios.c do_sdevio.c exception.c i8259.c oxpcie.c \
|
|
||||||
usermapped_data_arch.c \
|
|
||||||
io_inb.S io_inl.S io_intr.S io_inw.S io_outb.S io_outl.S io_outw.S \
|
|
||||||
usermapped_glo_ipc.S
|
|
||||||
|
|
||||||
OBJS.kernel+= ${UNPAGED_OBJS}
|
|
||||||
|
|
||||||
.ifdef CONFIG_SMP
|
|
||||||
SRCS += arch_smp.c trampoline.S
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_ACPI} != "no"
|
|
||||||
SRCS+= acpi.c
|
|
||||||
CPPFLAGS+= -DUSE_ACPI
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_APIC} != "no"
|
|
||||||
SRCS+= apic.c apic_asm.S
|
|
||||||
CPPFLAGS+= -DUSE_APIC
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_DEBUGREG} != "no"
|
|
||||||
SRCS+= breakpoints.c debugreg.S
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_WATCHDOG} != "no"
|
|
||||||
SRCS+= arch_watchdog.c
|
|
||||||
CPPFLAGS+= -DUSE_WATCHDOG
|
|
||||||
.endif
|
|
||||||
|
|
||||||
klib.o mpx.o head.o: procoffsets.h
|
|
||||||
|
|
||||||
SRCS+= procoffsets.h
|
|
||||||
|
|
||||||
PROCOFFSETSCF=procoffsets.cf
|
|
||||||
|
|
||||||
.PATH: ${NETBSDSRCDIR}/minix/include/arch/${MACHINE_ARCH}/include
|
|
||||||
|
|
||||||
procoffsets.h: ${PROCOFFSETSCF} kernel.h proc.h stackframe.h archtypes.h
|
|
||||||
${_MKTARGET_CREATE}
|
|
||||||
${TOOL_CAT} ${HERE}/${PROCOFFSETSCF} | \
|
|
||||||
${TOOL_GENASSYM} -- ${CC} ${CFLAGS:N-Wa,*} \
|
|
||||||
${CPPFLAGS} ${PROF} ${${USE_BITCODE:Uno} == "yes":? -fno-lto:} \
|
|
||||||
${GENASSYM_CPPFLAGS} >$@.tmp && \
|
|
||||||
mv -f $@.tmp $@
|
|
||||||
|
|
||||||
sconst.h: procoffsets.h
|
|
||||||
apic_asm.o head.o klib.o mpx.o: sconst.h
|
|
||||||
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
# Makefile for libmagicrt
|
|
||||||
#
|
|
||||||
# The magic runtime library is the runtime state transfer component for live
|
|
||||||
# update. It is not a regular library. First of all, it is only built when
|
|
||||||
# MKMAGIC is enabled, which also implies that we are building bitcode.
|
|
||||||
# Second, the produced file is a single bitcode object containing basically the
|
|
||||||
# concatenation of the individual bitcode objects produced from the source
|
|
||||||
# files. The final bitcode object is used to link against system services
|
|
||||||
# during the rest of the compilation process. It is installed only because
|
|
||||||
# this makes it easier to refer to it during the rest of the compilation
|
|
||||||
# process. However, both the library's generation and its installation cannot
|
|
||||||
# be done with regular bsd.lib.mk rules, which is why this Makefile is rather
|
|
||||||
# nonstandard.
|
|
||||||
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
LIBNAME= libmagicrt.bcc
|
|
||||||
|
|
||||||
SRCS= magic.c magic_analysis.c magic_asr.c magic_ds.c
|
|
||||||
SRCS+= magic_eval.c magic_eval_lib.c magic_mem.c magic_range.c
|
|
||||||
SRCS+= magic_selement.c magic_sentry.c magic_splay_tree.c
|
|
||||||
SRCS+= magic_st.c magic_util.c
|
|
||||||
|
|
||||||
OBJS= ${SRCS:.c=.bc}
|
|
||||||
|
|
||||||
CPPFLAGS+= -D__MINIX -D_MINIX_SYSTEM -D_SYSTEM
|
|
||||||
CPPFLAGS+= -I${.CURDIR}/include
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix/llvm/include # for magic_common.h
|
|
||||||
CPPFLAGS.magic_ds.c+= -I${NETBSDSRCDIR}/minix/servers # for ds/store.h
|
|
||||||
|
|
||||||
# XXX: there is essential code in assert() statements, so force asserts on..
|
|
||||||
CPPFLAGS+= -UNDEBUG
|
|
||||||
|
|
||||||
# All functions and data must be assigned to nonstandard sections. However,
|
|
||||||
# the magic_st module has different rules from the rest.
|
|
||||||
SECTIONIFY= -sectionify-no-override \
|
|
||||||
-sectionify-data-section-map=^_____magic_instr_.*/magic_instr_data,.*/magic_data \
|
|
||||||
-sectionify-function-section-map=.*/magic_functions
|
|
||||||
SECTIONIFY.magic_st.c= \
|
|
||||||
-sectionify-data-section-map=.*/magic_data_st \
|
|
||||||
-sectionify-function-section-map=.*/magic_functions_st
|
|
||||||
|
|
||||||
# HACK: keep the "temporary" .bc.o files generated as part of running the
|
|
||||||
# sectionify pass, so that we can trick clang into taking bitcode objects
|
|
||||||
# during the linking phase, because its driver refuses to take objects with
|
|
||||||
# the .bc suffix even when using the gold plugin. This works only because
|
|
||||||
# we are using sectionify on all objects here, and because bsd.lib.mk uses
|
|
||||||
# temporary names convenient for us. See also the comment in bsd.lib.mk.
|
|
||||||
SECTIONIFYMV=cp
|
|
||||||
|
|
||||||
LDFLAGS+=-nostdlib -rdynamic -shared -Wl,--plugin \
|
|
||||||
-Wl,${NETBSDSRCDIR}/minix/llvm/bin/LLVMgold.so \
|
|
||||||
-Wl,-plugin-opt=-disable-fp-elim -Wl,-plugin-opt=emit-llvm
|
|
||||||
|
|
||||||
realall: ${LIBNAME}
|
|
||||||
|
|
||||||
${LIBNAME}: ${OBJS}
|
|
||||||
${_MKTARGET_LINK}
|
|
||||||
${LINK.c} -o ${.TARGET} ${OBJS:.bc=.bc.o}
|
|
||||||
|
|
||||||
# The following block is a modified copy of similar blocks in bsd.lib.mk.
|
|
||||||
_LIB=${DESTDIR}/${LIBDIR}/${LIBNAME}
|
|
||||||
libinstall:: ${_LIB}
|
|
||||||
.PRECIOUS: ${_LIB}
|
|
||||||
.if ${MKUPDATE} == "no"
|
|
||||||
.if !defined(BUILD) && !make(all) && !make(${LIBNAME})
|
|
||||||
${_LIB}! .MADE
|
|
||||||
.endif
|
|
||||||
${_LIB}! ${LIBNAME} __archiveinstall
|
|
||||||
.else
|
|
||||||
.if !defined(BUILD) && !make(all) && !make(${LIBNAME})
|
|
||||||
${_LIB}: .MADE
|
|
||||||
.endif
|
|
||||||
${_LIB}: ${LIBNAME} __archiveinstall
|
|
||||||
.endif
|
|
||||||
|
|
||||||
CLEANFILES+= ${LIBNAME} ${OBJS:.bc=.bc.o}
|
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
|
||||||
|
|
@ -1,346 +0,0 @@
|
||||||
#
|
|
||||||
# Minimal libc for servers and drivers.
|
|
||||||
#
|
|
||||||
|
|
||||||
# LSC: TODO: Explaination of how this works
|
|
||||||
#.for f in \
|
|
||||||
#${f} ${f:C/\.o/.bc/}: ${NETBSDSRCDIR}//${f:C/\.o/.S/}
|
|
||||||
#OBJS+= ${f} ${f:C/\.o/.bc/}
|
|
||||||
#CLEANFILES+= ${f} ${f:C/\.o/.bc/}
|
|
||||||
#
|
|
||||||
#.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
#OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
#CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
#.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
|
|
||||||
#.endfor
|
|
||||||
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
LIB= minc
|
|
||||||
|
|
||||||
MKPIC= no # Never used as a dynamic library
|
|
||||||
|
|
||||||
LIBSADIR= ${NETBSDSRCDIR}/sys/lib/libsa
|
|
||||||
LIBSYSDIR= ${NETBSDSRCDIR}/minix/lib/libsys
|
|
||||||
LIBMINIXCDIR= ${NETBSDSRCDIR}/minix/lib/libc
|
|
||||||
LIBMINIXCARCHDIR= ${NETBSDSRCDIR}/minix/lib/libc/arch/${MACHINE_CPU}
|
|
||||||
LIBCDIR= ${NETBSDSRCDIR}/lib/libc
|
|
||||||
LIBCARCHDIR= ${LIBCDIR}/arch/${MACHINE_CPU}
|
|
||||||
LIBCOMMONCDIR= ${NETBSDSRCDIR}/common/lib/libc
|
|
||||||
LIBCOMMONCARCHDIR= ${LIBCOMMONCDIR}/arch/${MACHINE_CPU}
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${LIBCDIR}/include -I${LIBCDIR}
|
|
||||||
CPPFLAGS+= -D_LIBMINC
|
|
||||||
|
|
||||||
CFLAGS+= -fno-builtin
|
|
||||||
|
|
||||||
#
|
|
||||||
# Customized versions of libc functions.
|
|
||||||
#
|
|
||||||
SRCS+= atoi.c fputs.c _snprintf.c strtol.c
|
|
||||||
CPPFLAGS._snprintf.c+= -I${LIBSADIR}
|
|
||||||
|
|
||||||
CPPFLAGS.strtol.c+= -D_STANDALONE
|
|
||||||
CPPFLAGS.strtol.c+= -I${LIBCOMMONCDIR}/stdlib
|
|
||||||
CPPFLAGS.strtol.c+= -I${NETBSDSRCDIR}/sys
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Functions imported from libsa (StandAlone)
|
|
||||||
#
|
|
||||||
.for f in \
|
|
||||||
errno.o printf.o strerror.o subr_prf.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBSADIR}/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBSADIR}/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.fslib.c+= -I${NETBSDSRCDIR}/minix/fs
|
|
||||||
|
|
||||||
# LSC: I would like not to have to copy te file, but I can't
|
|
||||||
# have libsa appear first in the .PATH, for a lot of files
|
|
||||||
# would be taken from there, which is not the intention ATM.
|
|
||||||
CPPFLAGS.strerror.c+= -I${LIBSADIR}
|
|
||||||
|
|
||||||
# LSC: putchar and kputc have the same role / signature.
|
|
||||||
CPPFLAGS.subr_prf.c+= -Dputchar=kputc
|
|
||||||
|
|
||||||
# Activate optional support, may be deactivated.
|
|
||||||
CPPFLAGS.subr_prf.c+= -DLIBSA_PRINTF_LONGLONG_SUPPORT
|
|
||||||
CPPFLAGS.subr_prf.c+= -DLIBSA_PRINTF_WIDTH_SUPPORT
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Imports from libsys
|
|
||||||
#
|
|
||||||
.for f in \
|
|
||||||
kputc.o sys_diagctl.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBSYSDIR}/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBSYSDIR}/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Shared libc with userspace (/common/lib/libc)
|
|
||||||
#
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
bswap64.o rb.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCOMMONCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.rb.c+= -D_LIBC
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
sha2.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCOMMONCDIR}/hash/sha2/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/hash/sha2/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.sha2.c+= -I${NETBSDSRCDIR}/sys
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
ashrdi3.o divdi3.o lshrdi3.o moddi3.o \
|
|
||||||
udivdi3.o umoddi3.o umodsi3.o udivsi3.o \
|
|
||||||
udivmoddi4.o divsi3.o modsi3.o divmoddi4.o \
|
|
||||||
divmodsi4.o udivmodsi4.o #qdivrem.o lshldi3.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
COPTS+= -Wno-missing-prototypes \
|
|
||||||
-Wno-old-style-definition \
|
|
||||||
-Wno-strict-prototypes \
|
|
||||||
-Wno-uninitialized \
|
|
||||||
-Wno-cast-qual
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
random.o strtoul.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCOMMONCDIR}/stdlib/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/stdlib/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.strtoul.c+= -D_STANDALONE
|
|
||||||
CPPFLAGS.strtoul.c+= -I${NETBSDSRCDIR}/sys
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
strcasecmp.o strcspn.o strncasecmp.o strnlen.o strlcat.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCOMMONCDIR}/string/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/string/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Functions imported directly from libc.
|
|
||||||
#
|
|
||||||
|
|
||||||
.include "${.CURDIR}/arch/${MACHINE_ARCH}/Makefile.libc.inc"
|
|
||||||
|
|
||||||
# This file is specifically generated
|
|
||||||
SRCS+= errlist.c
|
|
||||||
|
|
||||||
errlist.c: ${LIBCDIR}/gen/errlist.awk \
|
|
||||||
${NETBSDSRCDIR}/sys/sys/errno.h
|
|
||||||
${TOOL_CAT} ${NETBSDSRCDIR}/sys/sys/errno.h | ${TOOL_SED} 's/(_SIGN//' | ${TOOL_AWK} -f ${LIBCDIR}/gen/errlist.awk > ${.TARGET}
|
|
||||||
CLEANFILES+= errlist.c
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
_errno.o \
|
|
||||||
getprogname.o setprogname.o execle.o sleep.o time.o \
|
|
||||||
ctype_.o tolower_.o toupper_.o usleep.o waitpid.o sigsetops.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.ctype_.c+= -I${LIBCDIR}/locale
|
|
||||||
CPPFLAGS.isctype.c+= -I${LIBCDIR}/locale
|
|
||||||
CPPFLAGS.tolower_.c+= -I${LIBCDIR}/locale
|
|
||||||
CPPFLAGS.toupper_.c+= -I${LIBCDIR}/locale
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
read_tsc_64.o fslib.o itoa.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBMINIXCDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
initfini.o stack_protector.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/misc/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/misc/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.stack_protector.c+= -Dxprintf=printf
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
regcomp.o regerror.o regexec.o regfree.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/regex/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/regex/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
abort.o atexit.o _env.o exit.o getenv.o \
|
|
||||||
ldiv.o malloc.o setenv.o \
|
|
||||||
reallocarr.o _rand48.o lrand48.o srand48.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/stdlib/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/stdlib/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
|
|
||||||
# LSC FIXME: Try to get this out of the loop
|
|
||||||
CPPFLAGS.${i}+= -I${LIBCDIR}/stdlib
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.malloc.c+= -D_LIBSYS
|
|
||||||
# Avoid magic instrumentation of the malloc data variables, since the heap is
|
|
||||||
# reconstructed upon state transfer. We do need to instrument the malloc
|
|
||||||
# functions, since we need to hook their mmap/munmap calls.
|
|
||||||
SECTIONIFY.malloc.c+= -sectionify-no-override \
|
|
||||||
-sectionify-data-section-map=.*/magic_malloc_data
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
strdup.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/string/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/string/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
access.o brk.o close.o environ.o execve.o fork.o fsync.o \
|
|
||||||
getgid.o getpid.o geteuid.o getuid.o gettimeofday.o getvfsstat.o \
|
|
||||||
init.o kernel_utils.o kill.o link.o loadname.o lseek.o _mcontext.o \
|
|
||||||
minix_rs.o mknod.o mmap.o nanosleep.o open.o pread.o pwrite.o read.o \
|
|
||||||
sbrk.o select.o setuid.o sigprocmask.o stack_utils.o \
|
|
||||||
stat.o stime.o svrctl.o syscall.o __sysctl.o _ucontext.o umask.o \
|
|
||||||
unlink.o wait4.o write.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
brksize.o _do_kernel_call_intr.o ipc_minix_kerninfo.o _ipc.o ucontext.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCARCHDIR}/sys/${f:C/\.o/.S/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBMINIXCARCHDIR}/sys/${f:C/\.o/.S/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
localtime.o
|
|
||||||
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/time/${f:C/\.o/.c/}
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/time/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
|
|
||||||
.if ${USE_BITCODE:Uno} == "yes"
|
|
||||||
OBJS+= ${f:C/\.o/.bc/}
|
|
||||||
CLEANFILES+= ${f:C/\.o/.bc/}
|
|
||||||
.endif # ${USE_BITCODE:Uno} == "yes"
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
ARCHSUBDIR=${MACHINE_CPU}
|
|
||||||
.include "${NETBSDSRCDIR}/minix/lib/libc/arch/${MACHINE_CPU}/Makefile.inc"
|
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Shared libc with userspace (/common/lib/libc)
|
|
||||||
#
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
memchr.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/string/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
lshldi3.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCDIR}/quad/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
byte_swap_2.o byte_swap_4.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCARCHDIR}/gen/${f:C/\.o/.S/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
__aeabi_idiv0.o __aeabi_ldiv0.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCARCHDIR}/gen/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.__aeabi_idiv0.c+= -D_STANDALONE -I${NETBSDSRCDIR}/sys
|
|
||||||
CPPFLAGS.__aeabi_ldiv0.c+= -D_STANDALONE -I${NETBSDSRCDIR}/sys
|
|
||||||
|
|
||||||
#.for f in \
|
|
||||||
# __aeabi_ldivmod.o __aeabi_uldivmod.o
|
|
||||||
#${f} ${f:C/\.o/.go/}: ${LIBCOMMONCARCHDIR}/quad/${f:C/\.o/.S/}
|
|
||||||
#OBJS+= ${f}
|
|
||||||
#CLEANFILES+= ${f}
|
|
||||||
#.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
memcmp.o memcpy.o memmove.o memset.o \
|
|
||||||
strcat.o strchr.o strcmp.o strcpy.o strlcpy.o strlen.o strncpy.o \
|
|
||||||
strncmp.o strrchr.o strcpy_arm.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCOMMONCARCHDIR}/string/${f:C/\.o/.S/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
CPPFLAGS.strcpy_arm.S+= -DSTRLCPY -D_LIBC
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# Functions imported directly from libc.
|
|
||||||
#
|
|
||||||
.for f in \
|
|
||||||
alloca.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCARCHDIR}/gen/${f:C/\.o/.S/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
__aeabi_dcmpeq.o __aeabi_fcmpeq.o \
|
|
||||||
__aeabi_dcmpge.o __aeabi_fcmpge.o \
|
|
||||||
__aeabi_dcmpgt.o __aeabi_fcmpgt.o \
|
|
||||||
__aeabi_dcmple.o __aeabi_fcmple.o \
|
|
||||||
__aeabi_dcmplt.o __aeabi_fcmplt.o \
|
|
||||||
__aeabi_dcmpun.o __aeabi_fcmpun.o
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -I${LIBCARCHDIR}/softfloat -I${LIBCDIR}/softfloat
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -DSOFTFLOAT_FOR_GCC
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCARCHDIR}/softfloat/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
aeabi_uidivmod.o aeabi_ldivmod.o aeabi_uldivmod.o aeabi_idivmod.o
|
|
||||||
${f} ${f:C/\.o/.go/}: ${NETBSDSRCDIR}/sys/external/bsd/compiler_rt/dist/lib/builtins/arm/${f:C/\.o/.S/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.for f in \
|
|
||||||
fpgetround.o fpsetround.o fpgetmask.o fpsetmask.o \
|
|
||||||
fpgetsticky.o fpsetsticky.o
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -I${LIBCARCHDIR}/softfloat -I${LIBCDIR}/softfloat
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -DSOFTFLOAT_FOR_GCC
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/softfloat/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
SOFTFLOAT_BITS?=64
|
|
||||||
.for f in \
|
|
||||||
softfloat.o
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -I${LIBCARCHDIR}/softfloat -I${LIBCDIR}/softfloat
|
|
||||||
CPPFLAGS.${f:C/\.o/.c/}+= -DSOFTFLOAT_FOR_GCC
|
|
||||||
${f} ${f:C/\.o/.go/}: ${LIBCDIR}/softfloat/bits${SOFTFLOAT_BITS}/${f:C/\.o/.c/}
|
|
||||||
OBJS+= ${f}
|
|
||||||
CLEANFILES+= ${f}
|
|
||||||
.endfor
|
|
||||||
|
|
||||||
.if defined(HAVE_GCC) && ${HAVE_GCC} >= 45
|
|
||||||
.if (${MACHINE_CPU} == "arm")
|
|
||||||
COPTS.softfloat.c+= -Wno-enum-compare -fno-tree-vrp
|
|
||||||
.endif
|
|
||||||
.endif
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,9 +0,0 @@
|
||||||
# Makefile for libsockevent
|
|
||||||
|
|
||||||
CPPFLAGS+= -D_MINIX_SYSTEM -I${NETBSDSRCDIR}/minix/lib/libcharevent
|
|
||||||
|
|
||||||
LIB= sockevent
|
|
||||||
|
|
||||||
SRCS= sockevent.c sockevent_proc.c
|
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#define _SYSTEM 1 /* get OK and negative error codes */
|
|
||||||
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <minix/config.h>
|
|
||||||
#include <minix/ipc.h>
|
|
||||||
#include <minix/endpoint.h>
|
|
||||||
#include <minix/sysutil.h>
|
|
||||||
#include <minix/const.h>
|
|
||||||
#include <minix/type.h>
|
|
||||||
#include <minix/syslib.h>
|
|
||||||
#include <minix/rmib.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/sem.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/queue.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <machine/param.h>
|
|
||||||
#include <machine/vm.h>
|
|
||||||
#include <machine/vmparam.h>
|
|
||||||
|
|
||||||
#include <lib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On NetBSD, these macros are only defined when _KERNEL is set. However,
|
|
||||||
* since ipcs(1) uses IXSEQ_TO_IPCID, NetBSD cannot change these macros without
|
|
||||||
* breaking the userland API. Thus, having a copy of them here is not risky.
|
|
||||||
*/
|
|
||||||
#define IPCID_TO_IX(id) ((id) & 0xffff)
|
|
||||||
#define IPCID_TO_SEQ(id) (((id) >> 16) & 0xffff)
|
|
||||||
|
|
||||||
/* main.c */
|
|
||||||
void update_sem_sub(int);
|
|
||||||
|
|
||||||
/* shm.c */
|
|
||||||
int do_shmget(message *);
|
|
||||||
int do_shmat(message *);
|
|
||||||
int do_shmdt(message *);
|
|
||||||
int do_shmctl(message *);
|
|
||||||
int get_shm_mib_info(struct rmib_oldp *);
|
|
||||||
int is_shm_nil(void);
|
|
||||||
void update_refcount_and_destroy(void);
|
|
||||||
|
|
||||||
/* sem.c */
|
|
||||||
int do_semget(message *);
|
|
||||||
int do_semctl(message *);
|
|
||||||
int do_semop(message *);
|
|
||||||
int get_sem_mib_info(struct rmib_oldp *);
|
|
||||||
int is_sem_nil(void);
|
|
||||||
void sem_process_event(endpoint_t, int);
|
|
||||||
|
|
||||||
/* utility.c */
|
|
||||||
int check_perm(struct ipc_perm *, endpoint_t, int);
|
|
||||||
void prepare_mib_perm(struct ipc_perm_sysctl *, const struct ipc_perm *);
|
|
||||||
|
|
@ -1,284 +0,0 @@
|
||||||
#include "inc.h"
|
|
||||||
|
|
||||||
#define SEM_EVENTS 0x01 /* semaphore code wants process events */
|
|
||||||
static unsigned int event_mask = 0;
|
|
||||||
|
|
||||||
static int verbose = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The call table for this service.
|
|
||||||
*/
|
|
||||||
#define CALL(n) [((n) - IPC_BASE)]
|
|
||||||
static int (* const call_vec[])(message *) = {
|
|
||||||
CALL(IPC_SHMGET) = do_shmget,
|
|
||||||
CALL(IPC_SHMAT) = do_shmat,
|
|
||||||
CALL(IPC_SHMDT) = do_shmdt,
|
|
||||||
CALL(IPC_SHMCTL) = do_shmctl,
|
|
||||||
CALL(IPC_SEMGET) = do_semget,
|
|
||||||
CALL(IPC_SEMCTL) = do_semctl,
|
|
||||||
CALL(IPC_SEMOP) = do_semop,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remote MIB implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. This
|
|
||||||
* function handles all queries on the "kern.ipc.sysvipc_info" sysctl(2) node.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
kern_ipc_info(struct rmib_call * call, struct rmib_node * node __unused,
|
|
||||||
struct rmib_oldp * oldp, struct rmib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (call->call_namelen != 1)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Let each IPC submodule provide information through it own function.
|
|
||||||
* An important security note: unlike IPC_STAT and the like, access to
|
|
||||||
* the sysvipc_info node does not require root privileges. That is: on
|
|
||||||
* NetBSD, any user can get a full listing of all IPC objects in the
|
|
||||||
* system. We therefore do not perform any security check here.
|
|
||||||
*/
|
|
||||||
switch (call->call_name[0]) {
|
|
||||||
case KERN_SYSVIPC_SEM_INFO:
|
|
||||||
return get_sem_mib_info(oldp);
|
|
||||||
|
|
||||||
case KERN_SYSVIPC_SHM_INFO:
|
|
||||||
return get_shm_mib_info(oldp);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The CTL_KERN KERN_SYSVIPC subtree. */
|
|
||||||
static struct rmib_node kern_ipc_table[] = {
|
|
||||||
/* 1*/ [KERN_SYSVIPC_INFO] = RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0,
|
|
||||||
kern_ipc_info, "sysvipc_info",
|
|
||||||
"System V style IPC information"),
|
|
||||||
/* 2*/ [KERN_SYSVIPC_MSG] = RMIB_INT(RMIB_RO, 0, "sysvmsg", "System V "
|
|
||||||
"style message support available"),
|
|
||||||
/* 3*/ [KERN_SYSVIPC_SEM] = RMIB_INT(RMIB_RO, 1, "sysvsem", "System V "
|
|
||||||
"style semaphore support available"),
|
|
||||||
/* 4*/ [KERN_SYSVIPC_SHM] = RMIB_INT(RMIB_RO, 1, "sysvshm", "System V "
|
|
||||||
"style shared memory support available"),
|
|
||||||
/* 5*/ /* KERN_SYSVIPC_SHMMAX: not yet supported */
|
|
||||||
/* 6*/ /* KERN_SYSVIPC_SHMMNI: not yet supported */
|
|
||||||
/* 7*/ /* KERN_SYSVIPC_SHMSEG: not yet supported */
|
|
||||||
/* 8*/ /* KERN_SYSVIPC_SHMMAXPGS: not yet supported */
|
|
||||||
/* 9*/ /* KERN_SYSVIPC_SHMUSEPHYS: not yet supported */
|
|
||||||
/* In addition, NetBSD has a number of dynamic nodes here. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The CTL_KERN KERN_SYSVIPC node. */
|
|
||||||
static struct rmib_node kern_ipc_node =
|
|
||||||
RMIB_NODE(RMIB_RO, kern_ipc_table, "ipc", "SysV IPC options");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the IPC server.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
sef_cb_init_fresh(int type __unused, sef_init_info_t * info __unused)
|
|
||||||
{
|
|
||||||
const int mib[] = { CTL_KERN, KERN_SYSVIPC };
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Register our own "kern.ipc" subtree with the MIB service.
|
|
||||||
*
|
|
||||||
* This call only returns local failures. Remote failures (in the MIB
|
|
||||||
* service) are silently ignored. So, we can safely panic on failure.
|
|
||||||
*/
|
|
||||||
if ((r = rmib_register(mib, __arraycount(mib), &kern_ipc_node)) != OK)
|
|
||||||
panic("unable to register remote MIB tree: %d", r);
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The service has received a signal.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
sef_cb_signal_handler(int signo)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Only check for termination signal, ignore anything else. */
|
|
||||||
if (signo != SIGTERM) return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if there are still IPC keys around. If not, we can safely
|
|
||||||
* exit immediately. Otherwise, warn the system administrator.
|
|
||||||
*/
|
|
||||||
if (is_sem_nil() && is_shm_nil()) {
|
|
||||||
rmib_deregister(&kern_ipc_node);
|
|
||||||
|
|
||||||
sef_exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("IPC: exit with unclean state\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SEF initialization.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
sef_local_startup(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Register init callbacks. */
|
|
||||||
sef_setcb_init_fresh(sef_cb_init_fresh);
|
|
||||||
sef_setcb_init_restart(sef_cb_init_fresh);
|
|
||||||
|
|
||||||
/* Register signal callbacks. */
|
|
||||||
sef_setcb_signal_handler(sef_cb_signal_handler);
|
|
||||||
|
|
||||||
/* Let SEF perform startup. */
|
|
||||||
sef_startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update the process event subscription mask if necessary, after one of the
|
|
||||||
* modules has changed its subscription needs. This code is set up so that
|
|
||||||
* support for SysV IPC message queues can be added easily later.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
update_sub(unsigned int new_mask)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* If the old and new mask are not both zero or nonzero, update. */
|
|
||||||
if (!event_mask != !new_mask) {
|
|
||||||
/*
|
|
||||||
* Subscribe to PM process events, or unsubscribe. While it
|
|
||||||
* might be tempting to implement a system that subscribes to
|
|
||||||
* events only from processes that are actually blocked (or
|
|
||||||
* using the SysV IPC facilities at all), this would result in
|
|
||||||
* race conditions where subscription could happen "too late"
|
|
||||||
* for an ongoing signal delivery, causing the affected process
|
|
||||||
* to deadlock. Subscribing to events from any other call is
|
|
||||||
* safe however, and we exploit that to limit the kernel-level
|
|
||||||
* message passing overhead in the common case (which is that
|
|
||||||
* the IPC servier is not being used at all). After we have
|
|
||||||
* unsubscribed, we may still get a few leftover events for the
|
|
||||||
* previous subscription, and we must properly reply to those.
|
|
||||||
*/
|
|
||||||
if (new_mask)
|
|
||||||
proceventmask(PROC_EVENT_EXIT | PROC_EVENT_SIGNAL);
|
|
||||||
else
|
|
||||||
proceventmask(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
event_mask = new_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update the process event subscription mask for the semaphore code.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
update_sem_sub(int want_events)
|
|
||||||
{
|
|
||||||
unsigned int new_mask;
|
|
||||||
|
|
||||||
new_mask = event_mask & ~SEM_EVENTS;
|
|
||||||
if (want_events)
|
|
||||||
new_mask |= SEM_EVENTS;
|
|
||||||
|
|
||||||
update_sub(new_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PM sent us a process event message. Handle it, and reply.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
got_proc_event(message * m)
|
|
||||||
{
|
|
||||||
endpoint_t endpt;
|
|
||||||
int r, has_exited;
|
|
||||||
|
|
||||||
endpt = m->m_pm_lsys_proc_event.endpt;
|
|
||||||
has_exited = (m->m_pm_lsys_proc_event.event == PROC_EVENT_EXIT);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Currently, only semaphore handling needs to know about processes
|
|
||||||
* being signaled and exiting.
|
|
||||||
*/
|
|
||||||
if (event_mask & SEM_EVENTS)
|
|
||||||
sem_process_event(endpt, has_exited);
|
|
||||||
|
|
||||||
/* Echo the request as a reply back to PM. */
|
|
||||||
m->m_type = PROC_EVENT_REPLY;
|
|
||||||
if ((r = asynsend3(m->m_source, m, AMF_NOREPLY)) != OK)
|
|
||||||
printf("IPC: replying to PM process event failed (%d)\n", r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The System V IPC server.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
main(int argc, char ** argv)
|
|
||||||
{
|
|
||||||
message m;
|
|
||||||
unsigned int call_index;
|
|
||||||
int r, ipc_status;
|
|
||||||
|
|
||||||
/* SEF local startup. */
|
|
||||||
env_setargs(argc, argv);
|
|
||||||
sef_local_startup();
|
|
||||||
|
|
||||||
/* The main message loop. */
|
|
||||||
for (;;) {
|
|
||||||
if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK)
|
|
||||||
panic("IPC: sef_receive_status failed: %d", r);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
printf("IPC: got %d from %d\n", m.m_type, m.m_source);
|
|
||||||
|
|
||||||
if (is_ipc_notify(ipc_status)) {
|
|
||||||
printf("IPC: ignoring notification from %d\n",
|
|
||||||
m.m_source);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process event messages from PM are handled separately. */
|
|
||||||
if (m.m_source == PM_PROC_NR && m.m_type == PROC_EVENT) {
|
|
||||||
got_proc_event(&m);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remote MIB messages from MIB are handled separately too. */
|
|
||||||
if (m.m_source == MIB_PROC_NR) {
|
|
||||||
rmib_process(&m, ipc_status);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dispatch the request. */
|
|
||||||
call_index = (unsigned int)(m.m_type - IPC_BASE);
|
|
||||||
|
|
||||||
if (call_index < __arraycount(call_vec) &&
|
|
||||||
call_vec[call_index] != NULL) {
|
|
||||||
r = call_vec[call_index](&m);
|
|
||||||
} else
|
|
||||||
r = ENOSYS;
|
|
||||||
|
|
||||||
/* Send a reply, if needed. */
|
|
||||||
if (r != SUSPEND) {
|
|
||||||
if (verbose)
|
|
||||||
printf("IPC: call result %d\n", r);
|
|
||||||
|
|
||||||
m.m_type = r;
|
|
||||||
/*
|
|
||||||
* Other fields may have been set by the handler
|
|
||||||
* function already.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((r = ipc_sendnb(m.m_source, &m)) != OK)
|
|
||||||
printf("IPC: send error %d\n", r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX there must be a better way to do this! */
|
|
||||||
update_refcount_and_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTREACHED */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,888 +0,0 @@
|
||||||
#include "inc.h"
|
|
||||||
|
|
||||||
struct sem_struct;
|
|
||||||
|
|
||||||
/* IPC-server process table, currently used for semaphores only. */
|
|
||||||
struct iproc {
|
|
||||||
struct sem_struct *ip_sem; /* affected semaphore set, or NULL */
|
|
||||||
struct sembuf *ip_sops; /* pending operations (malloc'ed) */
|
|
||||||
unsigned int ip_nsops; /* number of pending operations */
|
|
||||||
struct sembuf *ip_blkop; /* pointer to operation that blocked */
|
|
||||||
endpoint_t ip_endpt; /* process endpoint */
|
|
||||||
pid_t ip_pid; /* process PID */
|
|
||||||
TAILQ_ENTRY(iproc) ip_next; /* next waiting process */
|
|
||||||
} iproc[NR_PROCS];
|
|
||||||
|
|
||||||
struct semaphore {
|
|
||||||
unsigned short semval; /* semaphore value */
|
|
||||||
unsigned short semzcnt; /* # waiting for zero */
|
|
||||||
unsigned short semncnt; /* # waiting for increase */
|
|
||||||
pid_t sempid; /* process that did last op */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For the list of waiting processes, we use a doubly linked tail queue. In
|
|
||||||
* order to maintain a basic degree of fairness, we keep the pending processes
|
|
||||||
* in FCFS (well, at least first tested) order, which means we need to be able
|
|
||||||
* to add new processes at the end of the list. In order to remove waiting
|
|
||||||
* processes O(1) instead of O(n) we need a doubly linked list; in the common
|
|
||||||
* case we do have the element's predecessor, but STAILQ_REMOVE is O(n) anyway
|
|
||||||
* and NetBSD has no STAILQ_REMOVE_AFTER yet.
|
|
||||||
*
|
|
||||||
* We use one list per semaphore set: semop(2) affects only one semaphore set,
|
|
||||||
* but it may involve operations on multiple semaphores within the set. While
|
|
||||||
* it is possible to recheck only semaphores that were affected by a particular
|
|
||||||
* operation, and associate waiting lists to individual semaphores, the number
|
|
||||||
* of expected waiting processes is currently not high enough to justify the
|
|
||||||
* extra complexity of such an implementation.
|
|
||||||
*/
|
|
||||||
struct sem_struct {
|
|
||||||
struct semid_ds semid_ds;
|
|
||||||
struct semaphore sems[SEMMSL];
|
|
||||||
TAILQ_HEAD(waiters, iproc) waiters;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct sem_struct sem_list[SEMMNI];
|
|
||||||
static unsigned int sem_list_nr = 0; /* highest in-use slot number plus one */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find a semaphore set by key. The given key must not be IPC_PRIVATE. Return
|
|
||||||
* a pointer to the semaphore set if found, or NULL otherwise.
|
|
||||||
*/
|
|
||||||
static struct sem_struct *
|
|
||||||
sem_find_key(key_t key)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < sem_list_nr; i++) {
|
|
||||||
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
|
|
||||||
continue;
|
|
||||||
if (sem_list[i].semid_ds.sem_perm._key == key)
|
|
||||||
return &sem_list[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find a semaphore set by identifier. Return a pointer to the semaphore set
|
|
||||||
* if found, or NULL otherwise.
|
|
||||||
*/
|
|
||||||
static struct sem_struct *
|
|
||||||
sem_find_id(int id)
|
|
||||||
{
|
|
||||||
struct sem_struct *sem;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
i = IPCID_TO_IX(id);
|
|
||||||
if (i >= sem_list_nr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
sem = &sem_list[i];
|
|
||||||
if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
|
|
||||||
return NULL;
|
|
||||||
if (sem->semid_ds.sem_perm._seq != IPCID_TO_SEQ(id))
|
|
||||||
return NULL;
|
|
||||||
return sem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of the semget(2) system call.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
do_semget(message * m)
|
|
||||||
{
|
|
||||||
struct sem_struct *sem;
|
|
||||||
unsigned int i, seq;
|
|
||||||
key_t key;
|
|
||||||
int nsems, flag;
|
|
||||||
|
|
||||||
key = m->m_lc_ipc_semget.key;
|
|
||||||
nsems = m->m_lc_ipc_semget.nr;
|
|
||||||
flag = m->m_lc_ipc_semget.flag;
|
|
||||||
|
|
||||||
if (key != IPC_PRIVATE && (sem = sem_find_key(key)) != NULL) {
|
|
||||||
if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
|
|
||||||
return EEXIST;
|
|
||||||
if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, flag))
|
|
||||||
return EACCES;
|
|
||||||
if (nsems > sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
i = sem - sem_list;
|
|
||||||
} else {
|
|
||||||
if (key != IPC_PRIVATE && !(flag & IPC_CREAT))
|
|
||||||
return ENOENT;
|
|
||||||
if (nsems <= 0 || nsems > SEMMSL)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
/* Find a free entry. */
|
|
||||||
for (i = 0; i < __arraycount(sem_list); i++)
|
|
||||||
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
|
|
||||||
break;
|
|
||||||
if (i == __arraycount(sem_list))
|
|
||||||
return ENOSPC;
|
|
||||||
|
|
||||||
/* Initialize the entry. */
|
|
||||||
sem = &sem_list[i];
|
|
||||||
seq = sem->semid_ds.sem_perm._seq;
|
|
||||||
memset(sem, 0, sizeof(*sem));
|
|
||||||
sem->semid_ds.sem_perm._key = key;
|
|
||||||
sem->semid_ds.sem_perm.cuid =
|
|
||||||
sem->semid_ds.sem_perm.uid = getnuid(m->m_source);
|
|
||||||
sem->semid_ds.sem_perm.cgid =
|
|
||||||
sem->semid_ds.sem_perm.gid = getngid(m->m_source);
|
|
||||||
sem->semid_ds.sem_perm.mode = SEM_ALLOC | (flag & ACCESSPERMS);
|
|
||||||
sem->semid_ds.sem_perm._seq = (seq + 1) & 0x7fff;
|
|
||||||
sem->semid_ds.sem_nsems = nsems;
|
|
||||||
sem->semid_ds.sem_otime = 0;
|
|
||||||
sem->semid_ds.sem_ctime = clock_time(NULL);
|
|
||||||
TAILQ_INIT(&sem->waiters);
|
|
||||||
|
|
||||||
assert(i <= sem_list_nr);
|
|
||||||
if (i == sem_list_nr) {
|
|
||||||
/*
|
|
||||||
* If no semaphore sets were allocated before,
|
|
||||||
* subscribe to process events now.
|
|
||||||
*/
|
|
||||||
if (sem_list_nr == 0)
|
|
||||||
update_sem_sub(TRUE /*want_events*/);
|
|
||||||
|
|
||||||
sem_list_nr++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m->m_lc_ipc_semget.retid = IXSEQ_TO_IPCID(i, sem->semid_ds.sem_perm);
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Increase the proper suspension count (semncnt or semzcnt) of the semaphore
|
|
||||||
* on which the given process is blocked.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
inc_susp_count(struct iproc * ip)
|
|
||||||
{
|
|
||||||
struct sembuf *blkop;
|
|
||||||
struct semaphore *sp;
|
|
||||||
|
|
||||||
blkop = ip->ip_blkop;
|
|
||||||
sp = &ip->ip_sem->sems[blkop->sem_num];
|
|
||||||
|
|
||||||
if (blkop->sem_op != 0) {
|
|
||||||
assert(sp->semncnt < USHRT_MAX);
|
|
||||||
sp->semncnt++;
|
|
||||||
} else {
|
|
||||||
assert(sp->semncnt < USHRT_MAX);
|
|
||||||
sp->semzcnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decrease the proper suspension count (semncnt or semzcnt) of the semaphore
|
|
||||||
* on which the given process is blocked.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
dec_susp_count(struct iproc * ip)
|
|
||||||
{
|
|
||||||
struct sembuf *blkop;
|
|
||||||
struct semaphore *sp;
|
|
||||||
|
|
||||||
blkop = ip->ip_blkop;
|
|
||||||
sp = &ip->ip_sem->sems[blkop->sem_num];
|
|
||||||
|
|
||||||
if (blkop->sem_op != 0) {
|
|
||||||
assert(sp->semncnt > 0);
|
|
||||||
sp->semncnt--;
|
|
||||||
} else {
|
|
||||||
assert(sp->semzcnt > 0);
|
|
||||||
sp->semzcnt--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send a reply for a semop(2) call suspended earlier, thus waking up the
|
|
||||||
* process.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
send_reply(endpoint_t who, int ret)
|
|
||||||
{
|
|
||||||
message m;
|
|
||||||
|
|
||||||
memset(&m, 0, sizeof(m));
|
|
||||||
m.m_type = ret;
|
|
||||||
|
|
||||||
ipc_sendnb(who, &m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Satisfy or cancel the semop(2) call on which the given process is blocked,
|
|
||||||
* and send the given reply code (OK or a negative error code) to wake it up,
|
|
||||||
* unless the given code is EDONTREPLY.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
complete_semop(struct iproc * ip, int code)
|
|
||||||
{
|
|
||||||
struct sem_struct *sem;
|
|
||||||
|
|
||||||
sem = ip->ip_sem;
|
|
||||||
|
|
||||||
assert(sem != NULL);
|
|
||||||
|
|
||||||
TAILQ_REMOVE(&sem->waiters, ip, ip_next);
|
|
||||||
|
|
||||||
dec_susp_count(ip);
|
|
||||||
|
|
||||||
assert(ip->ip_sops != NULL);
|
|
||||||
free(ip->ip_sops);
|
|
||||||
|
|
||||||
ip->ip_sops = NULL;
|
|
||||||
ip->ip_blkop = NULL;
|
|
||||||
ip->ip_sem = NULL;
|
|
||||||
|
|
||||||
if (code != EDONTREPLY)
|
|
||||||
send_reply(ip->ip_endpt, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free up the given semaphore set. This includes cancelling any blocking
|
|
||||||
* semop(2) calls on any of its semaphores.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
remove_set(struct sem_struct * sem)
|
|
||||||
{
|
|
||||||
struct iproc *ip;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cancel all semop(2) operations on this semaphore set, with an EIDRM
|
|
||||||
* reply code.
|
|
||||||
*/
|
|
||||||
while (!TAILQ_EMPTY(&sem->waiters)) {
|
|
||||||
ip = TAILQ_FIRST(&sem->waiters);
|
|
||||||
|
|
||||||
complete_semop(ip, EIDRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark the entry as free. */
|
|
||||||
sem->semid_ds.sem_perm.mode &= ~SEM_ALLOC;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This may have been the last in-use slot in the list. Ensure that
|
|
||||||
* sem_list_nr again equals the highest in-use slot number plus one.
|
|
||||||
*/
|
|
||||||
while (sem_list_nr > 0 &&
|
|
||||||
!(sem_list[sem_list_nr - 1].semid_ds.sem_perm.mode & SEM_ALLOC))
|
|
||||||
sem_list_nr--;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this was our last semaphore set, unsubscribe from process events.
|
|
||||||
*/
|
|
||||||
if (sem_list_nr == 0)
|
|
||||||
update_sem_sub(FALSE /*want_events*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to perform a set of semaphore operations, as given by semop(2), on a
|
|
||||||
* semaphore set. The entire action must be atomic, i.e., either succeed in
|
|
||||||
* its entirety or fail without making any changes. Return OK on success, in
|
|
||||||
* which case the PIDs of all affected semaphores will be updated to the given
|
|
||||||
* 'pid' value, and the semaphore set's sem_otime will be updated as well.
|
|
||||||
* Return SUSPEND if the call should be suspended, in which case 'blkop' will
|
|
||||||
* be set to a pointer to the operation causing the call to block. Return an
|
|
||||||
* error code if the call failed altogether.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
try_semop(struct sem_struct *sem, struct sembuf *sops, unsigned int nsops,
|
|
||||||
pid_t pid, struct sembuf ** blkop)
|
|
||||||
{
|
|
||||||
struct semaphore *sp;
|
|
||||||
struct sembuf *op;
|
|
||||||
unsigned int i;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The operation must be processed atomically. However, it must also
|
|
||||||
* be processed "in array order," which we assume to mean that while
|
|
||||||
* processing one operation, the changes of the previous operations
|
|
||||||
* must be taken into account. This is relevant for cases where the
|
|
||||||
* same semaphore is referenced by more than one operation, for example
|
|
||||||
* to perform an atomic increase-if-zero action on a single semaphore.
|
|
||||||
* As a result, we must optimistically modify semaphore values and roll
|
|
||||||
* back on suspension or failure afterwards.
|
|
||||||
*/
|
|
||||||
r = OK;
|
|
||||||
op = NULL;
|
|
||||||
for (i = 0; i < nsops; i++) {
|
|
||||||
sp = &sem->sems[sops[i].sem_num];
|
|
||||||
op = &sops[i];
|
|
||||||
|
|
||||||
if (op->sem_op > 0) {
|
|
||||||
if (SEMVMX - sp->semval < op->sem_op) {
|
|
||||||
r = ERANGE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sp->semval += op->sem_op;
|
|
||||||
} else if (op->sem_op < 0) {
|
|
||||||
/*
|
|
||||||
* No SEMVMX check; if the process wants to deadlock
|
|
||||||
* itself by supplying -SEMVMX it is free to do so..
|
|
||||||
*/
|
|
||||||
if ((int)sp->semval < -(int)op->sem_op) {
|
|
||||||
r = (op->sem_flg & IPC_NOWAIT) ? EAGAIN :
|
|
||||||
SUSPEND;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sp->semval += op->sem_op;
|
|
||||||
} else /* (op->sem_op == 0) */ {
|
|
||||||
if (sp->semval != 0) {
|
|
||||||
r = (op->sem_flg & IPC_NOWAIT) ? EAGAIN :
|
|
||||||
SUSPEND;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we did not go through all the operations, then either an error
|
|
||||||
* occurred or the user process is to be suspended. In that case we
|
|
||||||
* must roll back any progress we have made so far, and return the
|
|
||||||
* operation that caused the call to block.
|
|
||||||
*/
|
|
||||||
if (i < nsops) {
|
|
||||||
assert(op != NULL);
|
|
||||||
*blkop = op;
|
|
||||||
|
|
||||||
/* Roll back all changes made so far. */
|
|
||||||
while (i-- > 0)
|
|
||||||
sem->sems[sops[i].sem_num].semval -= sops[i].sem_op;
|
|
||||||
|
|
||||||
assert(r != OK);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The operation has completed successfully. Also update all affected
|
|
||||||
* semaphores' PID values, and the semaphore set's last-semop time.
|
|
||||||
* The caller must do everything else.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < nsops; i++)
|
|
||||||
sem->sems[sops[i].sem_num].sempid = pid;
|
|
||||||
|
|
||||||
sem->semid_ds.sem_otime = clock_time(NULL);
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check whether any blocked operations can now be satisfied on any of the
|
|
||||||
* semaphores in the given semaphore set. Do this repeatedly as necessary, as
|
|
||||||
* any unblocked operation may in turn allow other operations to be resumed.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
check_set(struct sem_struct * sem)
|
|
||||||
{
|
|
||||||
struct iproc *ip, *nextip;
|
|
||||||
struct sembuf *blkop;
|
|
||||||
int r, woken_up;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Go through all the waiting processes in FIFO order, which is our
|
|
||||||
* best attempt at providing at least some fairness. Keep trying as
|
|
||||||
* long as we woke up at least one process, which means we made actual
|
|
||||||
* progress.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
woken_up = FALSE;
|
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(ip, &sem->waiters, ip_next, nextip) {
|
|
||||||
/* Retry the entire semop(2) operation, atomically. */
|
|
||||||
r = try_semop(ip->ip_sem, ip->ip_sops, ip->ip_nsops,
|
|
||||||
ip->ip_pid, &blkop);
|
|
||||||
|
|
||||||
if (r != SUSPEND) {
|
|
||||||
/* Success or failure. */
|
|
||||||
complete_semop(ip, r);
|
|
||||||
|
|
||||||
/* No changes are made on failure. */
|
|
||||||
if (r == OK)
|
|
||||||
woken_up = TRUE;
|
|
||||||
} else if (blkop != ip->ip_blkop) {
|
|
||||||
/*
|
|
||||||
* The process stays suspended, but it is now
|
|
||||||
* blocked on a different semaphore. As a
|
|
||||||
* result, we need to adjust the semaphores'
|
|
||||||
* suspension counts.
|
|
||||||
*/
|
|
||||||
dec_susp_count(ip);
|
|
||||||
|
|
||||||
ip->ip_blkop = blkop;
|
|
||||||
|
|
||||||
inc_susp_count(ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (woken_up);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fill a seminfo structure with actual information. The information returned
|
|
||||||
* depends on the given command, which may be either IPC_INFO or SEM_INFO.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
fill_seminfo(struct seminfo * sinfo, int cmd)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
assert(cmd == IPC_INFO || cmd == SEM_INFO);
|
|
||||||
|
|
||||||
memset(sinfo, 0, sizeof(*sinfo));
|
|
||||||
|
|
||||||
sinfo->semmap = SEMMNI;
|
|
||||||
sinfo->semmni = SEMMNI;
|
|
||||||
sinfo->semmns = SEMMNI * SEMMSL;
|
|
||||||
sinfo->semmnu = 0; /* TODO: support for SEM_UNDO */
|
|
||||||
sinfo->semmsl = SEMMSL;
|
|
||||||
sinfo->semopm = SEMOPM;
|
|
||||||
sinfo->semume = 0; /* TODO: support for SEM_UNDO */
|
|
||||||
if (cmd == SEM_INFO) {
|
|
||||||
/*
|
|
||||||
* For SEM_INFO the semusz field is expected to contain the
|
|
||||||
* number of semaphore sets currently in use.
|
|
||||||
*/
|
|
||||||
sinfo->semusz = sem_list_nr;
|
|
||||||
} else
|
|
||||||
sinfo->semusz = 0; /* TODO: support for SEM_UNDO */
|
|
||||||
sinfo->semvmx = SEMVMX;
|
|
||||||
if (cmd == SEM_INFO) {
|
|
||||||
/*
|
|
||||||
* For SEM_INFO the semaem field is expected to contain
|
|
||||||
* the total number of allocated semaphores.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < sem_list_nr; i++)
|
|
||||||
sinfo->semaem += sem_list[i].semid_ds.sem_nsems;
|
|
||||||
} else
|
|
||||||
sinfo->semaem = 0; /* TODO: support for SEM_UNDO */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of the semctl(2) system call.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
do_semctl(message * m)
|
|
||||||
{
|
|
||||||
static unsigned short valbuf[SEMMSL];
|
|
||||||
unsigned int i;
|
|
||||||
vir_bytes opt;
|
|
||||||
uid_t uid;
|
|
||||||
int r, id, num, cmd, val;
|
|
||||||
struct semid_ds tmp_ds;
|
|
||||||
struct sem_struct *sem;
|
|
||||||
struct seminfo sinfo;
|
|
||||||
|
|
||||||
id = m->m_lc_ipc_semctl.id;
|
|
||||||
num = m->m_lc_ipc_semctl.num;
|
|
||||||
cmd = m->m_lc_ipc_semctl.cmd;
|
|
||||||
opt = m->m_lc_ipc_semctl.opt;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Look up the target semaphore set. The IPC_INFO and SEM_INFO
|
|
||||||
* commands have no associated semaphore set. The SEM_STAT command
|
|
||||||
* takes an array index into the semaphore set table. For all other
|
|
||||||
* commands, look up the semaphore set by its given identifier.
|
|
||||||
* */
|
|
||||||
switch (cmd) {
|
|
||||||
case IPC_INFO:
|
|
||||||
case SEM_INFO:
|
|
||||||
sem = NULL;
|
|
||||||
break;
|
|
||||||
case SEM_STAT:
|
|
||||||
if (id < 0 || (unsigned int)id >= sem_list_nr)
|
|
||||||
return EINVAL;
|
|
||||||
sem = &sem_list[id];
|
|
||||||
if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
|
|
||||||
return EINVAL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if ((sem = sem_find_id(id)) == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the caller has the appropriate permissions on the target
|
|
||||||
* semaphore set. SETVAL and SETALL require write permission. IPC_SET
|
|
||||||
* and IPC_RMID require ownership permission, and return EPERM instead
|
|
||||||
* of EACCES on failure. IPC_INFO and SEM_INFO are free for general
|
|
||||||
* use. All other calls require read permission.
|
|
||||||
*/
|
|
||||||
switch (cmd) {
|
|
||||||
case SETVAL:
|
|
||||||
case SETALL:
|
|
||||||
assert(sem != NULL);
|
|
||||||
if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, IPC_W))
|
|
||||||
return EACCES;
|
|
||||||
break;
|
|
||||||
case IPC_SET:
|
|
||||||
case IPC_RMID:
|
|
||||||
assert(sem != NULL);
|
|
||||||
uid = getnuid(m->m_source);
|
|
||||||
if (uid != sem->semid_ds.sem_perm.cuid &&
|
|
||||||
uid != sem->semid_ds.sem_perm.uid && uid != 0)
|
|
||||||
return EPERM;
|
|
||||||
break;
|
|
||||||
case IPC_INFO:
|
|
||||||
case SEM_INFO:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(sem != NULL);
|
|
||||||
if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, IPC_R))
|
|
||||||
return EACCES;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case IPC_STAT:
|
|
||||||
case SEM_STAT:
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)&sem->semid_ds,
|
|
||||||
m->m_source, opt, sizeof(sem->semid_ds))) != OK)
|
|
||||||
return r;
|
|
||||||
if (cmd == SEM_STAT)
|
|
||||||
m->m_lc_ipc_semctl.ret =
|
|
||||||
IXSEQ_TO_IPCID(id, sem->semid_ds.sem_perm);
|
|
||||||
break;
|
|
||||||
case IPC_SET:
|
|
||||||
if ((r = sys_datacopy(m->m_source, opt, SELF,
|
|
||||||
(vir_bytes)&tmp_ds, sizeof(tmp_ds))) != OK)
|
|
||||||
return r;
|
|
||||||
sem->semid_ds.sem_perm.uid = tmp_ds.sem_perm.uid;
|
|
||||||
sem->semid_ds.sem_perm.gid = tmp_ds.sem_perm.gid;
|
|
||||||
sem->semid_ds.sem_perm.mode &= ~ACCESSPERMS;
|
|
||||||
sem->semid_ds.sem_perm.mode |=
|
|
||||||
tmp_ds.sem_perm.mode & ACCESSPERMS;
|
|
||||||
sem->semid_ds.sem_ctime = clock_time(NULL);
|
|
||||||
break;
|
|
||||||
case IPC_RMID:
|
|
||||||
/*
|
|
||||||
* Awaken all processes blocked in semop(2) on any semaphore in
|
|
||||||
* this set, and remove the semaphore set itself.
|
|
||||||
*/
|
|
||||||
remove_set(sem);
|
|
||||||
break;
|
|
||||||
case IPC_INFO:
|
|
||||||
case SEM_INFO:
|
|
||||||
fill_seminfo(&sinfo, cmd);
|
|
||||||
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source,
|
|
||||||
opt, sizeof(sinfo))) != OK)
|
|
||||||
return r;
|
|
||||||
/* Return the highest in-use slot number if any, or zero. */
|
|
||||||
if (sem_list_nr > 0)
|
|
||||||
m->m_lc_ipc_semctl.ret = sem_list_nr - 1;
|
|
||||||
else
|
|
||||||
m->m_lc_ipc_semctl.ret = 0;
|
|
||||||
break;
|
|
||||||
case GETALL:
|
|
||||||
assert(sem->semid_ds.sem_nsems <= __arraycount(valbuf));
|
|
||||||
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
|
|
||||||
valbuf[i] = sem->sems[i].semval;
|
|
||||||
r = sys_datacopy(SELF, (vir_bytes)valbuf, m->m_source,
|
|
||||||
opt, sizeof(unsigned short) * sem->semid_ds.sem_nsems);
|
|
||||||
if (r != OK)
|
|
||||||
return r;
|
|
||||||
break;
|
|
||||||
case GETNCNT:
|
|
||||||
if (num < 0 || num >= sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
m->m_lc_ipc_semctl.ret = sem->sems[num].semncnt;
|
|
||||||
break;
|
|
||||||
case GETPID:
|
|
||||||
if (num < 0 || num >= sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
m->m_lc_ipc_semctl.ret = sem->sems[num].sempid;
|
|
||||||
break;
|
|
||||||
case GETVAL:
|
|
||||||
if (num < 0 || num >= sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
m->m_lc_ipc_semctl.ret = sem->sems[num].semval;
|
|
||||||
break;
|
|
||||||
case GETZCNT:
|
|
||||||
if (num < 0 || num >= sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
m->m_lc_ipc_semctl.ret = sem->sems[num].semzcnt;
|
|
||||||
break;
|
|
||||||
case SETALL:
|
|
||||||
assert(sem->semid_ds.sem_nsems <= __arraycount(valbuf));
|
|
||||||
r = sys_datacopy(m->m_source, opt, SELF, (vir_bytes)valbuf,
|
|
||||||
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
|
|
||||||
if (r != OK)
|
|
||||||
return r;
|
|
||||||
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
|
|
||||||
if (valbuf[i] > SEMVMX)
|
|
||||||
return ERANGE;
|
|
||||||
#ifdef DEBUG_SEM
|
|
||||||
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
|
|
||||||
printf("SEMCTL: SETALL val: [%d] %d\n", i, valbuf[i]);
|
|
||||||
#endif
|
|
||||||
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
|
|
||||||
sem->sems[i].semval = valbuf[i];
|
|
||||||
sem->semid_ds.sem_ctime = clock_time(NULL);
|
|
||||||
/* Awaken any waiting parties if now possible. */
|
|
||||||
check_set(sem);
|
|
||||||
break;
|
|
||||||
case SETVAL:
|
|
||||||
val = (int)opt;
|
|
||||||
if (num < 0 || num >= sem->semid_ds.sem_nsems)
|
|
||||||
return EINVAL;
|
|
||||||
if (val < 0 || val > SEMVMX)
|
|
||||||
return ERANGE;
|
|
||||||
sem->sems[num].semval = val;
|
|
||||||
#ifdef DEBUG_SEM
|
|
||||||
printf("SEMCTL: SETVAL: %d %d\n", num, val);
|
|
||||||
#endif
|
|
||||||
sem->semid_ds.sem_ctime = clock_time(NULL);
|
|
||||||
/* Awaken any waiting parties if now possible. */
|
|
||||||
check_set(sem);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of the semop(2) system call.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
do_semop(message * m)
|
|
||||||
{
|
|
||||||
unsigned int i, mask, slot;
|
|
||||||
int id, r;
|
|
||||||
struct sembuf *sops, *blkop;
|
|
||||||
unsigned int nsops;
|
|
||||||
struct sem_struct *sem;
|
|
||||||
struct iproc *ip;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
id = m->m_lc_ipc_semop.id;
|
|
||||||
nsops = m->m_lc_ipc_semop.size;
|
|
||||||
|
|
||||||
if ((sem = sem_find_id(id)) == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if (nsops == 0)
|
|
||||||
return OK; /* nothing to do */
|
|
||||||
if (nsops > SEMOPM)
|
|
||||||
return E2BIG;
|
|
||||||
|
|
||||||
/* Get the array from the user process. */
|
|
||||||
sops = malloc(sizeof(sops[0]) * nsops);
|
|
||||||
if (sops == NULL)
|
|
||||||
return ENOMEM;
|
|
||||||
r = sys_datacopy(m->m_source, (vir_bytes)m->m_lc_ipc_semop.ops, SELF,
|
|
||||||
(vir_bytes)sops, sizeof(sops[0]) * nsops);
|
|
||||||
if (r != OK)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
#ifdef DEBUG_SEM
|
|
||||||
for (i = 0; i < nsops; i++)
|
|
||||||
printf("SEMOP: num:%d op:%d flg:%d\n",
|
|
||||||
sops[i].sem_num, sops[i].sem_op, sops[i].sem_flg);
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Check for permissions. We do this only once, even though the call
|
|
||||||
* might suspend and the semaphore set's permissions might be changed
|
|
||||||
* before the call resumes. The specification is not clear on this.
|
|
||||||
* Either way, perform the permission check before checking on the
|
|
||||||
* validity of semaphore numbers, since obtaining the semaphore set
|
|
||||||
* size itself requires read permission (except through sysctl(2)..).
|
|
||||||
*/
|
|
||||||
mask = 0;
|
|
||||||
for (i = 0; i < nsops; i++) {
|
|
||||||
if (sops[i].sem_op != 0)
|
|
||||||
mask |= IPC_W; /* check for write permission */
|
|
||||||
else
|
|
||||||
mask |= IPC_R; /* check for read permission */
|
|
||||||
}
|
|
||||||
r = EACCES;
|
|
||||||
if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, mask))
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
/* Check that all given semaphore numbers are within range. */
|
|
||||||
r = EFBIG;
|
|
||||||
for (i = 0; i < nsops; i++)
|
|
||||||
if (sops[i].sem_num >= sem->semid_ds.sem_nsems)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do not check if the same semaphore is referenced more than once
|
|
||||||
* (there was such a check here originally), because that is actually
|
|
||||||
* a valid case. The result is however that it is possible to
|
|
||||||
* construct a semop(2) request that will never complete, and thus,
|
|
||||||
* care must be taken that such requests do not create potential
|
|
||||||
* deadlock situations etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pid = getnpid(m->m_source);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not yet support SEM_UNDO at all, so we better not give the
|
|
||||||
* caller the impression that we do. For now, print a warning so that
|
|
||||||
* we know when an application actually fails for that reason.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < nsops; i++) {
|
|
||||||
if (sops[i].sem_flg & SEM_UNDO) {
|
|
||||||
/* Print a warning only if this isn't the test set.. */
|
|
||||||
if (sops[i].sem_flg != SHRT_MAX)
|
|
||||||
printf("IPC: pid %d tried to use SEM_UNDO\n",
|
|
||||||
pid);
|
|
||||||
r = EINVAL;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to perform the operation now. */
|
|
||||||
r = try_semop(sem, sops, nsops, pid, &blkop);
|
|
||||||
|
|
||||||
if (r == SUSPEND) {
|
|
||||||
/*
|
|
||||||
* The operation ended up blocking on a particular semaphore
|
|
||||||
* operation. Save all details in the slot for the user
|
|
||||||
* process, and add it to the list of processes waiting for
|
|
||||||
* this semaphore set.
|
|
||||||
*/
|
|
||||||
slot = _ENDPOINT_P(m->m_source);
|
|
||||||
assert(slot < __arraycount(iproc));
|
|
||||||
|
|
||||||
ip = &iproc[slot];
|
|
||||||
assert(ip->ip_sem == NULL); /* can't already be in use */
|
|
||||||
|
|
||||||
ip->ip_endpt = m->m_source;
|
|
||||||
ip->ip_pid = pid;
|
|
||||||
ip->ip_sem = sem;
|
|
||||||
ip->ip_sops = sops;
|
|
||||||
ip->ip_nsops = nsops;
|
|
||||||
ip->ip_blkop = blkop;
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(&sem->waiters, ip, ip_next);
|
|
||||||
|
|
||||||
inc_susp_count(ip);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
free(sops);
|
|
||||||
|
|
||||||
/* Awaken any other waiting parties if now possible. */
|
|
||||||
if (r == OK)
|
|
||||||
check_set(sem);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return semaphore information for a remote MIB call on the sysvipc_info node
|
|
||||||
* in the kern.ipc subtree. The particular semantics of this call are tightly
|
|
||||||
* coupled to the implementation of the ipcs(1) userland utility.
|
|
||||||
*/
|
|
||||||
ssize_t
|
|
||||||
get_sem_mib_info(struct rmib_oldp * oldp)
|
|
||||||
{
|
|
||||||
struct sem_sysctl_info semsi;
|
|
||||||
struct semid_ds *semds;
|
|
||||||
unsigned int i;
|
|
||||||
ssize_t r, off;
|
|
||||||
|
|
||||||
off = 0;
|
|
||||||
|
|
||||||
fill_seminfo(&semsi.seminfo, IPC_INFO);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* As a hackish exception, the requested size may imply that just
|
|
||||||
* general information is to be returned, without throwing an ENOMEM
|
|
||||||
* error because there is no space for full output.
|
|
||||||
*/
|
|
||||||
if (rmib_getoldlen(oldp) == sizeof(semsi.seminfo))
|
|
||||||
return rmib_copyout(oldp, 0, &semsi.seminfo,
|
|
||||||
sizeof(semsi.seminfo));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ipcs(1) blindly expects the returned array to be of size
|
|
||||||
* seminfo.semmni, using the SEM_ALLOC mode flag to see whether each
|
|
||||||
* entry is valid. If we return a smaller size, ipcs(1) will access
|
|
||||||
* arbitrary memory.
|
|
||||||
*/
|
|
||||||
assert(semsi.seminfo.semmni > 0);
|
|
||||||
|
|
||||||
if (oldp == NULL)
|
|
||||||
return sizeof(semsi) + sizeof(semsi.semids[0]) *
|
|
||||||
(semsi.seminfo.semmni - 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy out entries one by one. For the first entry, copy out the
|
|
||||||
* entire "semsi" structure. For subsequent entries, reuse the single
|
|
||||||
* embedded 'semids' element of "semsi" and copy out only that element.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < (unsigned int)semsi.seminfo.semmni; i++) {
|
|
||||||
semds = &sem_list[i].semid_ds;
|
|
||||||
|
|
||||||
memset(&semsi.semids[0], 0, sizeof(semsi.semids[0]));
|
|
||||||
if (i < sem_list_nr && (semds->sem_perm.mode & SEM_ALLOC)) {
|
|
||||||
prepare_mib_perm(&semsi.semids[0].sem_perm,
|
|
||||||
&semds->sem_perm);
|
|
||||||
semsi.semids[0].sem_nsems = semds->sem_nsems;
|
|
||||||
semsi.semids[0].sem_otime = semds->sem_otime;
|
|
||||||
semsi.semids[0].sem_ctime = semds->sem_ctime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (off == 0)
|
|
||||||
r = rmib_copyout(oldp, off, &semsi, sizeof(semsi));
|
|
||||||
else
|
|
||||||
r = rmib_copyout(oldp, off, &semsi.semids[0],
|
|
||||||
sizeof(semsi.semids[0]));
|
|
||||||
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
off += r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return TRUE iff no semaphore sets are allocated.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
is_sem_nil(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
return (sem_list_nr == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the given endpoint is blocked on a semop(2) call. If so, cancel
|
|
||||||
* the call, because either it is interrupted by a signal or the process was
|
|
||||||
* killed. In the former case, unblock the process by replying with EINTR.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sem_process_event(endpoint_t endpt, int has_exited)
|
|
||||||
{
|
|
||||||
unsigned int slot;
|
|
||||||
struct iproc *ip;
|
|
||||||
|
|
||||||
slot = _ENDPOINT_P(endpt);
|
|
||||||
assert(slot < __arraycount(iproc));
|
|
||||||
|
|
||||||
ip = &iproc[slot];
|
|
||||||
|
|
||||||
/* Was the process blocked on a semop(2) call at all? */
|
|
||||||
if (ip->ip_sem == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
assert(ip->ip_endpt == endpt);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It was; cancel the semop(2) call. If the process is being removed
|
|
||||||
* because its call was interrupted by a signal, then we must wake it
|
|
||||||
* up with EINTR.
|
|
||||||
*/
|
|
||||||
complete_semop(ip, has_exited ? EDONTREPLY : EINTR);
|
|
||||||
}
|
|
||||||
|
|
@ -1,469 +0,0 @@
|
||||||
#include "inc.h"
|
|
||||||
|
|
||||||
/* Private shm_perm.mode flags, synchronized with NetBSD kernel values */
|
|
||||||
#define SHM_ALLOC 0x0800 /* slot is in use (SHMSEG_ALLOCATED) */
|
|
||||||
|
|
||||||
struct shm_struct {
|
|
||||||
struct shmid_ds shmid_ds;
|
|
||||||
vir_bytes page;
|
|
||||||
phys_bytes vm_id;
|
|
||||||
};
|
|
||||||
static struct shm_struct shm_list[SHMMNI];
|
|
||||||
static unsigned int shm_list_nr = 0; /* highest in-use slot number plus one */
|
|
||||||
|
|
||||||
static struct shm_struct *
|
|
||||||
shm_find_key(key_t key)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if (key == IPC_PRIVATE)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < shm_list_nr; i++) {
|
|
||||||
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
continue;
|
|
||||||
if (shm_list[i].shmid_ds.shm_perm._key == key)
|
|
||||||
return &shm_list[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct shm_struct *
|
|
||||||
shm_find_id(int id)
|
|
||||||
{
|
|
||||||
struct shm_struct *shm;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
i = IPCID_TO_IX(id);
|
|
||||||
if (i >= shm_list_nr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
shm = &shm_list[i];
|
|
||||||
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
return NULL;
|
|
||||||
if (shm->shmid_ds.shm_perm._seq != IPCID_TO_SEQ(id))
|
|
||||||
return NULL;
|
|
||||||
return shm;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
do_shmget(message * m)
|
|
||||||
{
|
|
||||||
struct shm_struct *shm;
|
|
||||||
unsigned int i, seq;
|
|
||||||
key_t key;
|
|
||||||
size_t size, old_size;
|
|
||||||
int flag;
|
|
||||||
void *page;
|
|
||||||
|
|
||||||
key = m->m_lc_ipc_shmget.key;
|
|
||||||
old_size = size = m->m_lc_ipc_shmget.size;
|
|
||||||
flag = m->m_lc_ipc_shmget.flag;
|
|
||||||
|
|
||||||
if ((shm = shm_find_key(key)) != NULL) {
|
|
||||||
if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, flag))
|
|
||||||
return EACCES;
|
|
||||||
if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
|
|
||||||
return EEXIST;
|
|
||||||
if (size && shm->shmid_ds.shm_segsz < size)
|
|
||||||
return EINVAL;
|
|
||||||
i = shm - shm_list;
|
|
||||||
} else { /* no key found */
|
|
||||||
if (!(flag & IPC_CREAT))
|
|
||||||
return ENOENT;
|
|
||||||
if (size <= 0)
|
|
||||||
return EINVAL;
|
|
||||||
size = roundup(size, PAGE_SIZE);
|
|
||||||
if (size <= 0)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
/* Find a free entry. */
|
|
||||||
for (i = 0; i < __arraycount(shm_list); i++)
|
|
||||||
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
break;
|
|
||||||
if (i == __arraycount(shm_list))
|
|
||||||
return ENOSPC;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate memory to share. For now, we store the page
|
|
||||||
* reference as a numerical value so as to avoid issues with
|
|
||||||
* live update. TODO: a proper solution.
|
|
||||||
*/
|
|
||||||
page = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
|
|
||||||
if (page == MAP_FAILED)
|
|
||||||
return ENOMEM;
|
|
||||||
memset(page, 0, size);
|
|
||||||
|
|
||||||
/* Initialize the entry. */
|
|
||||||
shm = &shm_list[i];
|
|
||||||
seq = shm->shmid_ds.shm_perm._seq;
|
|
||||||
memset(shm, 0, sizeof(*shm));
|
|
||||||
|
|
||||||
shm->shmid_ds.shm_perm._key = key;
|
|
||||||
shm->shmid_ds.shm_perm.cuid =
|
|
||||||
shm->shmid_ds.shm_perm.uid = getnuid(m->m_source);
|
|
||||||
shm->shmid_ds.shm_perm.cgid =
|
|
||||||
shm->shmid_ds.shm_perm.gid = getngid(m->m_source);
|
|
||||||
shm->shmid_ds.shm_perm.mode = SHM_ALLOC | (flag & ACCESSPERMS);
|
|
||||||
shm->shmid_ds.shm_perm._seq = (seq + 1) & 0x7fff;
|
|
||||||
shm->shmid_ds.shm_segsz = old_size;
|
|
||||||
shm->shmid_ds.shm_atime = 0;
|
|
||||||
shm->shmid_ds.shm_dtime = 0;
|
|
||||||
shm->shmid_ds.shm_ctime = clock_time(NULL);
|
|
||||||
shm->shmid_ds.shm_cpid = getnpid(m->m_source);
|
|
||||||
shm->shmid_ds.shm_lpid = 0;
|
|
||||||
shm->shmid_ds.shm_nattch = 0;
|
|
||||||
shm->page = (vir_bytes)page;
|
|
||||||
shm->vm_id = vm_getphys(sef_self(), page);
|
|
||||||
|
|
||||||
assert(i <= shm_list_nr);
|
|
||||||
if (i == shm_list_nr)
|
|
||||||
shm_list_nr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
m->m_lc_ipc_shmget.retid = IXSEQ_TO_IPCID(i, shm->shmid_ds.shm_perm);
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
do_shmat(message * m)
|
|
||||||
{
|
|
||||||
int id, flag, mask;
|
|
||||||
vir_bytes addr;
|
|
||||||
void *ret;
|
|
||||||
struct shm_struct *shm;
|
|
||||||
|
|
||||||
id = m->m_lc_ipc_shmat.id;
|
|
||||||
addr = (vir_bytes)m->m_lc_ipc_shmat.addr;
|
|
||||||
flag = m->m_lc_ipc_shmat.flag;
|
|
||||||
|
|
||||||
if (addr % PAGE_SIZE) {
|
|
||||||
if (flag & SHM_RND)
|
|
||||||
addr -= addr % PAGE_SIZE;
|
|
||||||
else
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((shm = shm_find_id(id)) == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
mask = 0;
|
|
||||||
if (flag & SHM_RDONLY)
|
|
||||||
mask = IPC_R;
|
|
||||||
else
|
|
||||||
mask = IPC_R | IPC_W;
|
|
||||||
if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, mask))
|
|
||||||
return EACCES;
|
|
||||||
|
|
||||||
ret = vm_remap(m->m_source, sef_self(), (void *)addr,
|
|
||||||
(void *)shm->page, shm->shmid_ds.shm_segsz);
|
|
||||||
if (ret == MAP_FAILED)
|
|
||||||
return ENOMEM;
|
|
||||||
|
|
||||||
shm->shmid_ds.shm_atime = clock_time(NULL);
|
|
||||||
shm->shmid_ds.shm_lpid = getnpid(m->m_source);
|
|
||||||
/* nattch is updated lazily */
|
|
||||||
|
|
||||||
m->m_lc_ipc_shmat.retaddr = ret;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
update_refcount_and_destroy(void)
|
|
||||||
{
|
|
||||||
u8_t rc;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < shm_list_nr; i++) {
|
|
||||||
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rc = vm_getrefcount(sef_self(), (void *)shm_list[i].page);
|
|
||||||
if (rc == (u8_t)-1) {
|
|
||||||
printf("IPC: can't find physical region.\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
shm_list[i].shmid_ds.shm_nattch = rc - 1;
|
|
||||||
|
|
||||||
if (shm_list[i].shmid_ds.shm_nattch == 0 &&
|
|
||||||
(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
|
|
||||||
munmap((void *)shm_list[i].page,
|
|
||||||
roundup(shm_list[i].shmid_ds.shm_segsz,
|
|
||||||
PAGE_SIZE));
|
|
||||||
/* Mark the entry as free. */
|
|
||||||
shm_list[i].shmid_ds.shm_perm.mode &= ~SHM_ALLOC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now that we may have removed an arbitrary set of slots, ensure that
|
|
||||||
* shm_list_nr again equals the highest in-use slot number plus one.
|
|
||||||
*/
|
|
||||||
while (shm_list_nr > 0 &&
|
|
||||||
!(shm_list[shm_list_nr - 1].shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
shm_list_nr--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
do_shmdt(message * m)
|
|
||||||
{
|
|
||||||
struct shm_struct *shm;
|
|
||||||
vir_bytes addr;
|
|
||||||
phys_bytes vm_id;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
addr = (vir_bytes)m->m_lc_ipc_shmdt.addr;
|
|
||||||
|
|
||||||
if ((vm_id = vm_getphys(m->m_source, (void *)addr)) == 0)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
for (i = 0; i < shm_list_nr; i++) {
|
|
||||||
shm = &shm_list[i];
|
|
||||||
|
|
||||||
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (shm->vm_id == vm_id) {
|
|
||||||
shm->shmid_ds.shm_atime = clock_time(NULL);
|
|
||||||
shm->shmid_ds.shm_lpid = getnpid(m->m_source);
|
|
||||||
/* nattch is updated lazily */
|
|
||||||
|
|
||||||
vm_unmap(m->m_source, (void *)addr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == shm_list_nr)
|
|
||||||
printf("IPC: do_shmdt: ID %lu not found\n", vm_id);
|
|
||||||
|
|
||||||
update_refcount_and_destroy();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fill a shminfo structure with actual information.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
fill_shminfo(struct shminfo * sinfo)
|
|
||||||
{
|
|
||||||
|
|
||||||
memset(sinfo, 0, sizeof(*sinfo));
|
|
||||||
|
|
||||||
sinfo->shmmax = (unsigned long)-1;
|
|
||||||
sinfo->shmmin = 1;
|
|
||||||
sinfo->shmmni = __arraycount(shm_list);
|
|
||||||
sinfo->shmseg = (unsigned long)-1;
|
|
||||||
sinfo->shmall = (unsigned long)-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
do_shmctl(message * m)
|
|
||||||
{
|
|
||||||
struct shmid_ds tmp_ds;
|
|
||||||
struct shm_struct *shm;
|
|
||||||
struct shminfo sinfo;
|
|
||||||
struct shm_info s_info;
|
|
||||||
vir_bytes buf;
|
|
||||||
unsigned int i;
|
|
||||||
uid_t uid;
|
|
||||||
int r, id, cmd;
|
|
||||||
|
|
||||||
id = m->m_lc_ipc_shmctl.id;
|
|
||||||
cmd = m->m_lc_ipc_shmctl.cmd;
|
|
||||||
buf = (vir_bytes)m->m_lc_ipc_shmctl.buf;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For stat calls, sure that all information is up-to-date. Since this
|
|
||||||
* may free the slot, do this before mapping from ID to slot below.
|
|
||||||
*/
|
|
||||||
if (cmd == IPC_STAT || cmd == SHM_STAT)
|
|
||||||
update_refcount_and_destroy();
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case IPC_INFO:
|
|
||||||
case SHM_INFO:
|
|
||||||
shm = NULL;
|
|
||||||
break;
|
|
||||||
case SHM_STAT:
|
|
||||||
if (id < 0 || (unsigned int)id >= shm_list_nr)
|
|
||||||
return EINVAL;
|
|
||||||
shm = &shm_list[id];
|
|
||||||
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
return EINVAL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if ((shm = shm_find_id(id)) == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case IPC_STAT:
|
|
||||||
case SHM_STAT:
|
|
||||||
/* Check whether the caller has read permission. */
|
|
||||||
if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, IPC_R))
|
|
||||||
return EACCES;
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
|
|
||||||
m->m_source, buf, sizeof(shm->shmid_ds))) != OK)
|
|
||||||
return r;
|
|
||||||
if (cmd == SHM_STAT)
|
|
||||||
m->m_lc_ipc_shmctl.ret =
|
|
||||||
IXSEQ_TO_IPCID(id, shm->shmid_ds.shm_perm);
|
|
||||||
break;
|
|
||||||
case IPC_SET:
|
|
||||||
uid = getnuid(m->m_source);
|
|
||||||
if (uid != shm->shmid_ds.shm_perm.cuid &&
|
|
||||||
uid != shm->shmid_ds.shm_perm.uid && uid != 0)
|
|
||||||
return EPERM;
|
|
||||||
if ((r = sys_datacopy(m->m_source, buf, SELF,
|
|
||||||
(vir_bytes)&tmp_ds, sizeof(tmp_ds))) != OK)
|
|
||||||
return r;
|
|
||||||
shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
|
|
||||||
shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
|
|
||||||
shm->shmid_ds.shm_perm.mode &= ~ACCESSPERMS;
|
|
||||||
shm->shmid_ds.shm_perm.mode |=
|
|
||||||
tmp_ds.shm_perm.mode & ACCESSPERMS;
|
|
||||||
shm->shmid_ds.shm_ctime = clock_time(NULL);
|
|
||||||
break;
|
|
||||||
case IPC_RMID:
|
|
||||||
uid = getnuid(m->m_source);
|
|
||||||
if (uid != shm->shmid_ds.shm_perm.cuid &&
|
|
||||||
uid != shm->shmid_ds.shm_perm.uid && uid != 0)
|
|
||||||
return EPERM;
|
|
||||||
shm->shmid_ds.shm_perm.mode |= SHM_DEST;
|
|
||||||
/* Destroy if possible. */
|
|
||||||
update_refcount_and_destroy();
|
|
||||||
break;
|
|
||||||
case IPC_INFO:
|
|
||||||
fill_shminfo(&sinfo);
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source,
|
|
||||||
buf, sizeof(sinfo))) != OK)
|
|
||||||
return r;
|
|
||||||
if (shm_list_nr > 0)
|
|
||||||
m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
|
|
||||||
else
|
|
||||||
m->m_lc_ipc_shmctl.ret = 0;
|
|
||||||
break;
|
|
||||||
case SHM_INFO:
|
|
||||||
memset(&s_info, 0, sizeof(s_info));
|
|
||||||
s_info.used_ids = shm_list_nr;
|
|
||||||
s_info.shm_tot = 0;
|
|
||||||
for (i = 0; i < shm_list_nr; i++)
|
|
||||||
s_info.shm_tot +=
|
|
||||||
shm_list[i].shmid_ds.shm_segsz / PAGE_SIZE;
|
|
||||||
s_info.shm_rss = s_info.shm_tot;
|
|
||||||
s_info.shm_swp = 0;
|
|
||||||
s_info.swap_attempts = 0;
|
|
||||||
s_info.swap_successes = 0;
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)&s_info, m->m_source,
|
|
||||||
buf, sizeof(s_info))) != OK)
|
|
||||||
return r;
|
|
||||||
if (shm_list_nr > 0)
|
|
||||||
m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
|
|
||||||
else
|
|
||||||
m->m_lc_ipc_shmctl.ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return shared memory information for a remote MIB call on the sysvipc_info
|
|
||||||
* node in the kern.ipc subtree. The particular semantics of this call are
|
|
||||||
* tightly coupled to the implementation of the ipcs(1) userland utility.
|
|
||||||
*/
|
|
||||||
ssize_t
|
|
||||||
get_shm_mib_info(struct rmib_oldp * oldp)
|
|
||||||
{
|
|
||||||
struct shm_sysctl_info shmsi;
|
|
||||||
struct shmid_ds *shmds;
|
|
||||||
unsigned int i;
|
|
||||||
ssize_t r, off;
|
|
||||||
|
|
||||||
off = 0;
|
|
||||||
|
|
||||||
fill_shminfo(&shmsi.shminfo);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* As a hackish exception, the requested size may imply that just
|
|
||||||
* general information is to be returned, without throwing an ENOMEM
|
|
||||||
* error because there is no space for full output.
|
|
||||||
*/
|
|
||||||
if (rmib_getoldlen(oldp) == sizeof(shmsi.shminfo))
|
|
||||||
return rmib_copyout(oldp, 0, &shmsi.shminfo,
|
|
||||||
sizeof(shmsi.shminfo));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ipcs(1) blindly expects the returned array to be of size
|
|
||||||
* shminfo.shmmni, using the SHMSEG_ALLOCATED (aka SHM_ALLOC) mode flag
|
|
||||||
* to see whether each entry is valid. If we return a smaller size,
|
|
||||||
* ipcs(1) will access arbitrary memory.
|
|
||||||
*/
|
|
||||||
assert(shmsi.shminfo.shmmni > 0);
|
|
||||||
|
|
||||||
if (oldp == NULL)
|
|
||||||
return sizeof(shmsi) + sizeof(shmsi.shmids[0]) *
|
|
||||||
(shmsi.shminfo.shmmni - 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy out entries one by one. For the first entry, copy out the
|
|
||||||
* entire "shmsi" structure. For subsequent entries, reuse the single
|
|
||||||
* embedded 'shmids' element of "shmsi" and copy out only that element.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < shmsi.shminfo.shmmni; i++) {
|
|
||||||
shmds = &shm_list[i].shmid_ds;
|
|
||||||
|
|
||||||
memset(&shmsi.shmids[0], 0, sizeof(shmsi.shmids[0]));
|
|
||||||
if (i < shm_list_nr && (shmds->shm_perm.mode & SHM_ALLOC)) {
|
|
||||||
prepare_mib_perm(&shmsi.shmids[0].shm_perm,
|
|
||||||
&shmds->shm_perm);
|
|
||||||
shmsi.shmids[0].shm_segsz = shmds->shm_segsz;
|
|
||||||
shmsi.shmids[0].shm_lpid = shmds->shm_lpid;
|
|
||||||
shmsi.shmids[0].shm_cpid = shmds->shm_cpid;
|
|
||||||
shmsi.shmids[0].shm_atime = shmds->shm_atime;
|
|
||||||
shmsi.shmids[0].shm_dtime = shmds->shm_dtime;
|
|
||||||
shmsi.shmids[0].shm_ctime = shmds->shm_ctime;
|
|
||||||
shmsi.shmids[0].shm_nattch = shmds->shm_nattch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (off == 0)
|
|
||||||
r = rmib_copyout(oldp, off, &shmsi, sizeof(shmsi));
|
|
||||||
else
|
|
||||||
r = rmib_copyout(oldp, off, &shmsi.shmids[0],
|
|
||||||
sizeof(shmsi.shmids[0]));
|
|
||||||
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
off += r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void
|
|
||||||
list_shm_ds(void)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
printf("key\tid\tpage\n");
|
|
||||||
for (i = 0; i < shm_list_nr; i++) {
|
|
||||||
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
|
|
||||||
continue;
|
|
||||||
printf("%ld\t%d\t%lx\n",
|
|
||||||
shm_list[i].shmid_ds.shm_perm._key,
|
|
||||||
IXSEQ_TO_IPCID(i, shm_list[i].shmid_ds.shm_perm),
|
|
||||||
shm_list[i].page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
|
||||||
is_shm_nil(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
return (shm_list_nr == 0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# Makefile for Information Server (IS)
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= is
|
|
||||||
SRCS= main.c dmp.c dmp_kernel.c dmp_pm.c dmp_fs.c dmp_rs.c dmp_ds.c dmp_vm.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS}
|
|
||||||
LDADD+= -lsys
|
|
||||||
|
|
||||||
CPPFLAGS.dmp_fs.c+= -I${NETBSDSRCDIR}/minix/servers \
|
|
||||||
-I${NETBSDSRCDIR}/minix/fs
|
|
||||||
CPPFLAGS.dmp_kernel.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS.dmp_rs.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS.dmp_vm.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
# This setting must match the kernel's, as it affects the IRQ hooks table size.
|
|
||||||
.if ${USE_APIC} != "no"
|
|
||||||
CFLAGS+= -DUSE_APIC
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# Makefile for the Management Information Base (MIB) server
|
|
||||||
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
.if !empty(DBG:M-Og) || !empty(CFLAGS:M-Og) || \
|
|
||||||
!empty(DBG:M-g) || !empty(CFLAGS:M-g)
|
|
||||||
#LSC: -Wno-maybe-uninitialized while compiling with -DNDEBUG -Og
|
|
||||||
CWARNFLAGS.gcc+= -Wno-maybe-uninitialized
|
|
||||||
.endif
|
|
||||||
|
|
||||||
PROG= mib
|
|
||||||
SRCS= main.c tree.c remote.c kern.c vm.c hw.c proc.c minix.c
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS}
|
|
||||||
LDADD+= -lsys
|
|
||||||
|
|
||||||
WARNS?= 5
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,508 +0,0 @@
|
||||||
/* MIB service - kern.c - implementation of the CTL_KERN subtree */
|
|
||||||
|
|
||||||
#include "mib.h"
|
|
||||||
|
|
||||||
#include <sys/svrctl.h>
|
|
||||||
#include <minix/sysinfo.h>
|
|
||||||
#include <machine/partition.h>
|
|
||||||
|
|
||||||
#include "servers/vfs/const.h"
|
|
||||||
#include "servers/vfs/dmap.h"
|
|
||||||
|
|
||||||
static char hostname[MAXHOSTNAMELEN], domainname[MAXHOSTNAMELEN];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verification for CTL_KERN KERN_SECURELVL.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mib_kern_securelvl(struct mib_call * call __unused, struct mib_node * node,
|
|
||||||
void * ptr, size_t size __unused)
|
|
||||||
{
|
|
||||||
int v;
|
|
||||||
|
|
||||||
memcpy(&v, ptr, sizeof(v));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only ever allow the security level to be increased. This is a mock
|
|
||||||
* implementation. TODO: implement actual support for security levels.
|
|
||||||
*/
|
|
||||||
return (v >= node->node_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_CLOCKRATE.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_clockrate(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
struct clockinfo clockinfo;
|
|
||||||
|
|
||||||
memset(&clockinfo, 0, sizeof(clockinfo));
|
|
||||||
|
|
||||||
clockinfo.hz = sys_hz();
|
|
||||||
clockinfo.tick = 1000000 / clockinfo.hz;
|
|
||||||
clockinfo.profhz = clockinfo.hz;
|
|
||||||
clockinfo.stathz = clockinfo.hz;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Number of microseconds that can be corrected per clock tick through
|
|
||||||
* adjtime(2). The kernel allows correction of one clock tick per
|
|
||||||
* clock tick, which means it should be the same as .tick.. I think.
|
|
||||||
* TODO: get this from the kernel itself.
|
|
||||||
*/
|
|
||||||
clockinfo.tickadj = clockinfo.tick;
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &clockinfo, sizeof(clockinfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_PROFILING.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_profiling(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp __unused,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* As per sysctl(7). We have a different profiling API. */
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_HARDCLOCK_TICKS.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_hardclock_ticks(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
int uptime;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The number of hardclock (hardware clock driver) ticks is what we
|
|
||||||
* call the number of monotonic clock ticks AKA the uptime clock ticks.
|
|
||||||
*/
|
|
||||||
uptime = (int)getticks();
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &uptime, sizeof(uptime));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_ROOT_DEVICE.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_root_device(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
char name[PATH_MAX];
|
|
||||||
struct sysgetenv sysgetenv;
|
|
||||||
|
|
||||||
sysgetenv.key = __UNCONST("rootdevname");
|
|
||||||
sysgetenv.keylen = strlen(sysgetenv.key) + 1;
|
|
||||||
sysgetenv.val = name;
|
|
||||||
sysgetenv.vallen = sizeof(name);
|
|
||||||
|
|
||||||
if (svrctl(PMGETPARAM, &sysgetenv) != 0)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
name[MIN(sysgetenv.vallen, sizeof(name) - 1)] = '\0';
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, name, strlen(name) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_CCPU.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_ccpu(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
int ccpu;
|
|
||||||
|
|
||||||
ccpu = (int)cpuavg_getccpu();
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &ccpu, sizeof(ccpu));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_CP_TIME.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_cp_time(struct mib_call * call, struct mib_node * node __unused,
|
|
||||||
struct mib_oldp * oldp, struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
uint64_t ticks[MINIX_CPUSTATES], sum[MINIX_CPUSTATES];
|
|
||||||
unsigned int cpu;
|
|
||||||
int i, r, do_sum;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a subnode is provided, it identifies the CPU number for which to
|
|
||||||
* return information. If no subnode is provided, but a size is given
|
|
||||||
* that allows returning information for all CPUs, return information
|
|
||||||
* for all of them in an array. If no such size is given either,
|
|
||||||
* return a summation of all CPU statistics. Both we and the kernel
|
|
||||||
* are considering the number of configured CPUs (hw.ncpu).
|
|
||||||
*/
|
|
||||||
if (call->call_namelen > 1)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if (call->call_namelen == 1) {
|
|
||||||
/* Do not bother saving on this call if oldp is NULL. */
|
|
||||||
if ((r = sys_getcputicks(ticks, call->call_name[0])) != OK)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, ticks, sizeof(ticks));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldp == NULL)
|
|
||||||
return sizeof(ticks); /* implying a summation request */
|
|
||||||
|
|
||||||
do_sum = (mib_getoldlen(oldp) == sizeof(ticks));
|
|
||||||
|
|
||||||
if (do_sum)
|
|
||||||
memset(&sum, 0, sizeof(sum));
|
|
||||||
|
|
||||||
for (cpu = 0; cpu < CONFIG_MAX_CPUS; cpu++) {
|
|
||||||
if ((r = sys_getcputicks(ticks, cpu)) != OK)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (do_sum) {
|
|
||||||
for (i = 0; i < MINIX_CPUSTATES; i++)
|
|
||||||
sum[i] += ticks[i];
|
|
||||||
} else {
|
|
||||||
if ((r = mib_copyout(oldp, cpu * sizeof(ticks), ticks,
|
|
||||||
sizeof(ticks))) < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (do_sum)
|
|
||||||
return mib_copyout(oldp, 0, sum, sizeof(sum));
|
|
||||||
else
|
|
||||||
return cpu * sizeof(ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_CONSDEV.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_consdev(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
dev_t dev;
|
|
||||||
|
|
||||||
dev = makedev(TTY_MAJOR, CONS_MINOR);
|
|
||||||
|
|
||||||
/* No support for legacy 32-bit requests. */
|
|
||||||
return mib_copyout(oldp, 0, &dev, sizeof(dev));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verification for CTL_KERN KERN_FORKFSLEEP.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mib_kern_forkfsleep(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, void * ptr, size_t size __unused)
|
|
||||||
{
|
|
||||||
int v;
|
|
||||||
|
|
||||||
memcpy(&v, ptr, sizeof(v));
|
|
||||||
|
|
||||||
return (v >= 0 && v <= MAXSLP * 1000); /* rules from NetBSD */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_DRIVERS.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_drivers(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
struct dmap dmap_tab[NR_DEVICES];
|
|
||||||
struct kinfo_drivers drivers[NR_DEVICES + 1];
|
|
||||||
unsigned int count;
|
|
||||||
devmajor_t maj;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On MINIX3, we list only drivers that are actually running.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (getsysinfo(VFS_PROC_NR, SI_DMAP_TAB, dmap_tab,
|
|
||||||
sizeof(dmap_tab)) != OK)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compatibility hack. NetBSD userland expects that the name of the
|
|
||||||
* PTY driver is "pts". Add an extra entry for this purpose if needed.
|
|
||||||
*/
|
|
||||||
if (dmap_tab[PTY_MAJOR].dmap_driver != NONE &&
|
|
||||||
strcmp(dmap_tab[PTY_MAJOR].dmap_label, "pts")) {
|
|
||||||
if (mib_inrange(oldp, 0)) {
|
|
||||||
memset(&drivers[0], 0, sizeof(drivers[0]));
|
|
||||||
strlcpy(drivers[count].d_name, "pts",
|
|
||||||
sizeof(drivers[0].d_name));
|
|
||||||
drivers[count].d_bmajor = -1;
|
|
||||||
drivers[count].d_cmajor = PTY_MAJOR;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (maj = 0; maj < NR_DEVICES; maj++) {
|
|
||||||
if (dmap_tab[maj].dmap_driver == NONE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mib_inrange(oldp, sizeof(drivers[0]) * count)) {
|
|
||||||
memset(&drivers[count], 0, sizeof(drivers[0]));
|
|
||||||
|
|
||||||
strlcpy(drivers[count].d_name,
|
|
||||||
dmap_tab[maj].dmap_label,
|
|
||||||
sizeof(drivers[0].d_name));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not know whether the device is a block device,
|
|
||||||
* character device, or both. In any case, a driver
|
|
||||||
* has only one major number.
|
|
||||||
*/
|
|
||||||
drivers[count].d_bmajor = maj;
|
|
||||||
drivers[count].d_cmajor = maj;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, drivers, count * sizeof(drivers[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_KERN KERN_BOOTTIME.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_boottime(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
memset(&tv, 0, sizeof(tv));
|
|
||||||
|
|
||||||
if (getuptime(NULL, NULL, &tv.tv_sec) != OK)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &tv, sizeof(tv));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mock implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. Normally,
|
|
||||||
* the IPC service overrides the entire "kern.ipc" subtree. Therefore, this
|
|
||||||
* function will only ever be called when the IPC service is *not* running.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_kern_ipc_info(struct mib_call * call, struct mib_node * node __unused,
|
|
||||||
struct mib_oldp * oldp __unused, struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* The caller must always specify the resouce type (sem/shm/msg). */
|
|
||||||
if (call->call_namelen != 1)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The CTL_KERN KERN_SYSVIPC nodes, when not overridden by the IPC service. */
|
|
||||||
static struct mib_node mib_kern_ipc_table[] = {
|
|
||||||
/* 1*/ [KERN_SYSVIPC_INFO] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_ipc_info, "sysvipc_info",
|
|
||||||
"System V style IPC information"),
|
|
||||||
/* 2*/ [KERN_SYSVIPC_MSG] = MIB_INT(_P | _RO, 0, "sysvmsg", "System V "
|
|
||||||
"style message support available"),
|
|
||||||
/* 3*/ [KERN_SYSVIPC_SEM] = MIB_INT(_P | _RO, 0, "sysvsem", "System V "
|
|
||||||
"style semaphore support available"),
|
|
||||||
/* 4*/ [KERN_SYSVIPC_SHM] = MIB_INT(_P | _RO, 0, "sysvshm", "System V "
|
|
||||||
"style shared memory support available"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The CTL_KERN nodes. */
|
|
||||||
static struct mib_node mib_kern_table[] = {
|
|
||||||
/* 1*/ [KERN_OSTYPE] = MIB_STRING(_P | _RO, OS_NAME, "ostype",
|
|
||||||
"Operating system type"),
|
|
||||||
/* 2*/ [KERN_OSRELEASE] = MIB_STRING(_P | _RO, OS_RELEASE, "osrelease",
|
|
||||||
"Operating system release"),
|
|
||||||
/* 3*/ [KERN_OSREV] = MIB_INT(_P | _RO , OS_REV, "osrevision",
|
|
||||||
"Operating system revision"),
|
|
||||||
/* 4*/ [KERN_VERSION] = MIB_STRING(_P | _RO, OS_VERSION, "version",
|
|
||||||
"Kernel version"),
|
|
||||||
/* 5*/ [KERN_MAXVNODES] = MIB_INT(_P | _RO, NR_VNODES, "maxvnodes",
|
|
||||||
"Maximum number of vnodes"),
|
|
||||||
/* 6*/ [KERN_MAXPROC] = MIB_INT(_P | _RO, NR_PROCS, "maxproc",
|
|
||||||
"Maximum number of simultaneous "
|
|
||||||
"processes"),
|
|
||||||
/* 7*/ [KERN_MAXFILES] = MIB_INT(_P | _RO, NR_VNODES, "maxfiles",
|
|
||||||
"Maximum number of open files"),
|
|
||||||
/* 8*/ [KERN_ARGMAX] = MIB_INT(_P | _RO, ARG_MAX, "argmax",
|
|
||||||
"Maximum number of bytes of arguments to "
|
|
||||||
"execve(2)"),
|
|
||||||
/* 9*/ [KERN_SECURELVL] = MIB_INTV(_P | _RW, -1, mib_kern_securelvl,
|
|
||||||
"securelevel", "System security level"),
|
|
||||||
/*10*/ [KERN_HOSTNAME] = MIB_STRING(_P | _RW, hostname, "hostname",
|
|
||||||
"System hostname"),
|
|
||||||
/*11*/ [KERN_HOSTID] = MIB_INT(_P | _RW | CTLFLAG_HEX, 0, "hostid",
|
|
||||||
"System host ID number"),
|
|
||||||
/*12*/ [KERN_CLOCKRATE] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
|
|
||||||
sizeof(struct clockinfo),
|
|
||||||
mib_kern_clockrate, "clockrate",
|
|
||||||
"Kernel clock rates"),
|
|
||||||
/*13*/ /* KERN_VNODE: not yet implemented */
|
|
||||||
/*14*/ /* KERN_PROC: not yet implemented */
|
|
||||||
/*15*/ /* KERN_FILE: not yet implemented */
|
|
||||||
/*16*/ [KERN_PROF] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_profiling, "profiling",
|
|
||||||
"Profiling information (not available)"),
|
|
||||||
/*17*/ [KERN_POSIX1] = MIB_INT(_P | _RO, _POSIX_VERSION,
|
|
||||||
"posix1version", "Version of ISO/IEC 9945 "
|
|
||||||
"(POSIX 1003.1) with which the operating "
|
|
||||||
"system attempts to comply"),
|
|
||||||
/*18*/ [KERN_NGROUPS] = MIB_INT(_P | _RO, NGROUPS_MAX, "ngroups",
|
|
||||||
"Maximum number of supplemental groups"),
|
|
||||||
/*19*/ [KERN_JOB_CONTROL] = MIB_INT(_P | _RO, 0, "job_control",
|
|
||||||
"Whether job control is available"),
|
|
||||||
/*20*/ [KERN_SAVED_IDS] = MIB_INT(_P | _RO, 0, "saved_ids",
|
|
||||||
"Whether POSIX saved set-group/user ID is "
|
|
||||||
"available"),
|
|
||||||
/*21*/ /* KERN_OBOOTTIME: obsolete */
|
|
||||||
/*22*/ [KERN_DOMAINNAME] = MIB_STRING(_P | _RW, domainname,
|
|
||||||
"domainname", "YP domain name"),
|
|
||||||
/*23*/ [KERN_MAXPARTITIONS] = MIB_INT(_P | _RO, NR_PARTITIONS,
|
|
||||||
"maxpartitions", "Maximum number of "
|
|
||||||
"partitions allowed per disk"),
|
|
||||||
/*24*/ /* KERN_RAWPARTITION: incompatible with our device node scheme */
|
|
||||||
/*25*/ /* KERN_NTPTIME: not yet supported */
|
|
||||||
/*26*/ /* KERN_TIMEX: not yet supported */
|
|
||||||
/*27*/ /* KERN_AUTONICETIME: not yet supported */
|
|
||||||
/*28*/ /* KERN_AUTONICEVAL: not yet supported */
|
|
||||||
/*29*/ [KERN_RTC_OFFSET] = MIB_INT(_P | _RW, 0, "rtc_offset", "Offset "
|
|
||||||
"of real time clock from UTC in minutes"),
|
|
||||||
/*30*/ [KERN_ROOT_DEVICE] = MIB_FUNC(_P | _RO | CTLTYPE_STRING, 0,
|
|
||||||
mib_kern_root_device, "root_device",
|
|
||||||
"Name of the root device"),
|
|
||||||
/*31*/ [KERN_MSGBUFSIZE] = MIB_INT(_P | _RO, DIAG_BUFSIZE, "msgbufsize",
|
|
||||||
"Size of the kernel message buffer"),
|
|
||||||
/*32*/ [KERN_FSYNC] = MIB_INT(_P | _RO, 1, "fsync", "Whether the "
|
|
||||||
"POSIX 1003.1b File Synchronization Option"
|
|
||||||
" is available on this system"),
|
|
||||||
/*33*/ /* KERN_OLDSYSVMSG: obsolete */
|
|
||||||
/*34*/ /* KERN_OLDSYSVSEM: obsolete */
|
|
||||||
/*35*/ /* KERN_OLDSYSVSHM: obsolete */
|
|
||||||
/*36*/ /* KERN_OLDSHORTCORENAME: obsolete */
|
|
||||||
/*37*/ [KERN_SYNCHRONIZED_IO] = MIB_INT(_P | _RO, 0, "synchronized_io",
|
|
||||||
"Whether the POSIX 1003.1b Synchronized "
|
|
||||||
"I/O Option is available on this system"),
|
|
||||||
/*38*/ [KERN_IOV_MAX] = MIB_INT(_P | _RO, IOV_MAX, "iov_max",
|
|
||||||
"Maximum number of iovec structures per "
|
|
||||||
"process"),
|
|
||||||
/*39*/ /* KERN_MBUF: not yet supported */
|
|
||||||
/*40*/ [KERN_MAPPED_FILES] = MIB_INT(_P | _RO, 1, "mapped_files",
|
|
||||||
"Whether the POSIX 1003.1b Memory Mapped "
|
|
||||||
"Files Option is available on this "
|
|
||||||
"system"),
|
|
||||||
/*41*/ [KERN_MEMLOCK] = MIB_INT(_P | _RO, 0, "memlock", "Whether "
|
|
||||||
"the POSIX 1003.1b Process Memory Locking "
|
|
||||||
"Option is available on this system"),
|
|
||||||
/*42*/ [KERN_MEMLOCK_RANGE] = MIB_INT(_P | _RO, 0, "memlock_range",
|
|
||||||
"Whether the POSIX 1003.1b Range Memory "
|
|
||||||
"Locking Option is available on this "
|
|
||||||
"system"),
|
|
||||||
/*43*/ [KERN_MEMORY_PROTECTION]= MIB_INT(_P | _RO, 0, "memory_protection",
|
|
||||||
"Whether the POSIX 1003.1b Memory "
|
|
||||||
"Protection Option is available on this "
|
|
||||||
"system"),
|
|
||||||
/*44*/ /* KERN_LOGIN_NAME_MAX: not yet supported */
|
|
||||||
/*45*/ /* KERN_DEFCORENAME: obsolete */
|
|
||||||
/*46*/ /* KERN_LOGSIGEXIT: not yet supported */
|
|
||||||
/*47*/ [KERN_PROC2] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_proc2, "proc2",
|
|
||||||
"Machine-independent process information"),
|
|
||||||
/*48*/ [KERN_PROC_ARGS] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_proc_args, "proc_args",
|
|
||||||
"Process argument information"),
|
|
||||||
/*49*/ [KERN_FSCALE] = MIB_INT(_P | _RO, FSCALE, "fscale",
|
|
||||||
"Kernel fixed-point scale factor"),
|
|
||||||
/*50*/ [KERN_CCPU] = MIB_FUNC(_P | _RO | CTLTYPE_INT, sizeof(int),
|
|
||||||
mib_kern_ccpu, "ccpu",
|
|
||||||
"Scheduler exponential decay value"),
|
|
||||||
/*51*/ [KERN_CP_TIME] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_cp_time, "cp_time", "Clock ticks "
|
|
||||||
"spent in different CPU states"),
|
|
||||||
/*52*/ /* KERN_OLDSYSVIPC_INFO: obsolete */
|
|
||||||
/*53*/ /* KERN_MSGBUF: not yet supported */
|
|
||||||
/*54*/ [KERN_CONSDEV] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
|
|
||||||
sizeof(dev_t), mib_kern_consdev, "consdev",
|
|
||||||
"Console device"),
|
|
||||||
/*55*/ [KERN_MAXPTYS] = MIB_INT(_P | _RO, NR_PTYS, "maxptys",
|
|
||||||
"Maximum number of pseudo-ttys"),
|
|
||||||
/*56*/ /* KERN_PIPE: not yet supported */
|
|
||||||
/*57*/ [KERN_MAXPHYS] = MIB_INT(_P | _RO, 4*1024*1024, "maxphys",
|
|
||||||
"Maximum raw I/O transfer size"),
|
|
||||||
/* 4MB is the upper limit for AHCI */
|
|
||||||
/*58*/ /* KERN_SBMAX: not yet supported */
|
|
||||||
/*59*/ /* KERN_TKSTAT: not yet supported */
|
|
||||||
/*60*/ [KERN_MONOTONIC_CLOCK] = MIB_INT(_P | _RO, _POSIX_MONOTONIC_CLOCK,
|
|
||||||
"monotonic_clock",
|
|
||||||
"Implementation version of the POSIX "
|
|
||||||
"1003.1b Monotonic Clock Option"),
|
|
||||||
/*61*/ /* KERN_URND: not yet supported */
|
|
||||||
/*62*/ /* KERN_LABELSECTOR: not yet supported */
|
|
||||||
/*63*/ /* KERN_LABELOFFSET: not yet supported */
|
|
||||||
/*64*/ [KERN_LWP] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
|
|
||||||
mib_kern_lwp, "lwp",
|
|
||||||
"System-wide LWP information"),
|
|
||||||
/*65*/ [KERN_FORKFSLEEP] = MIB_INTV(_P | _RW, 0, mib_kern_forkfsleep,
|
|
||||||
"forkfsleep", "Milliseconds to sleep on "
|
|
||||||
"fork failure due to process limits"),
|
|
||||||
/*66*/ /* KERN_POSIX_THREADS: not yet supported */
|
|
||||||
/*67*/ /* KERN_POSIX_SEMAPHORES: not yet supported */
|
|
||||||
/*68*/ /* KERN_POSIX_BARRIERS: not yet supported */
|
|
||||||
/*69*/ /* KERN_POSIX_TIMERS: not yet supported */
|
|
||||||
/*70*/ /* KERN_POSIX_SPIN_LOCKS: not yet supported */
|
|
||||||
/*71*/ /* KERN_POSIX_READER_WRITER_LOCKS: not yet supported */
|
|
||||||
/*72*/ [KERN_DUMP_ON_PANIC] = MIB_INT(_P | _RO, 0, "dump_on_panic",
|
|
||||||
"Perform a crash dump on system panic"),
|
|
||||||
/*73*/ /* KERN_SOMAXKVA: not yet supported */
|
|
||||||
/*74*/ /* KERN_ROOT_PARTITION: incompatible with our device node scheme */
|
|
||||||
/*75*/ [KERN_DRIVERS] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT, 0,
|
|
||||||
mib_kern_drivers, "drivers",
|
|
||||||
"List of all drivers with block and "
|
|
||||||
"character device numbers"),
|
|
||||||
/*76*/ /* KERN_BUF: not yet supported */
|
|
||||||
/*77*/ /* KERN_FILE2: not yet supported */
|
|
||||||
/*78*/ /* KERN_VERIEXEC: not yet supported */
|
|
||||||
/*79*/ /* KERN_CP_ID: not yet supported */
|
|
||||||
/*80*/ [KERN_HARDCLOCK_TICKS] = MIB_FUNC(_P | _RO | CTLFLAG_UNSIGNED |
|
|
||||||
CTLTYPE_INT, sizeof(int),
|
|
||||||
mib_kern_hardclock_ticks,
|
|
||||||
"hardclock_ticks",
|
|
||||||
"Number of hardclock ticks"),
|
|
||||||
/*81*/ /* KERN_ARND: not yet supported */
|
|
||||||
/*82*/ [KERN_SYSVIPC] = MIB_NODE(_P | _RO, mib_kern_ipc_table, "ipc",
|
|
||||||
"SysV IPC options"),
|
|
||||||
/*83*/ [KERN_BOOTTIME] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
|
|
||||||
sizeof(struct timeval), mib_kern_boottime,
|
|
||||||
"boottime", "System boot time"),
|
|
||||||
/*84*/ /* KERN_EVCNT: not yet supported */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the CTL_KERN subtree.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
mib_kern_init(struct mib_node * node)
|
|
||||||
{
|
|
||||||
|
|
||||||
MIB_INIT_ENODE(node, mib_kern_table);
|
|
||||||
}
|
|
||||||
|
|
@ -1,492 +0,0 @@
|
||||||
/* MIB service - main.c - request abstraction and first-level tree */
|
|
||||||
/*
|
|
||||||
* This is the Management Information Base (MIB) service. Its one and only
|
|
||||||
* task is to implement the sysctl(2) system call, which plays a fairly
|
|
||||||
* important role in parts of *BSD userland.
|
|
||||||
*
|
|
||||||
* The sysctl(2) interface is used to access a variety of information. In
|
|
||||||
* order to obtain that information, and possibly modify it, the MIB service
|
|
||||||
* calls into many other services. The MIB service must therefore not be
|
|
||||||
* called directly from other services, with the exception of ProcFS. In fact,
|
|
||||||
* ProcFS is currently the only service that is modeled as logically higher in
|
|
||||||
* the MINIX3 service stack than MIB, something that itself is possible only
|
|
||||||
* due to the nonblocking nature of VFS. MIB may issue blocking calls to VFS.
|
|
||||||
*
|
|
||||||
* The MIB service is in the boot image because even init(8) makes use of
|
|
||||||
* sysctl(2) during its own startup, so launching the MIB service at any later
|
|
||||||
* time would make a proper implementation of sysctl(2) impossible. Also, the
|
|
||||||
* service needs superuser privileges because it may need to issue privileged
|
|
||||||
* calls and obtain privileged information from other services.
|
|
||||||
*
|
|
||||||
* While most of the sysctl tree is maintained locally, the MIB service also
|
|
||||||
* allows other services to register "remote" subtrees which are then handled
|
|
||||||
* entirely by those services. This feature, which works much like file system
|
|
||||||
* mounting, allows 1) sysctl handling code to stay local to its corresponding
|
|
||||||
* service, and 2) parts of the sysctl tree to adapt and expand dynamically as
|
|
||||||
* optional services are started and stopped. Compared to the MIB service's
|
|
||||||
* local handling, remotely handled subtrees are subject to several additional
|
|
||||||
* practical restrictions, hoever. In the current implementation, the MIB
|
|
||||||
* service makes blocking calls to remote services as needed; in the future,
|
|
||||||
* these interactions could be made (more) asynchronous.
|
|
||||||
*
|
|
||||||
* The MIB service was created by David van Moolenbroek <david@minix3.org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mib.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Most of these initially empty nodes are filled in by their corresponding
|
|
||||||
* modules' _init calls; see mib_init below. However, some subtrees are not
|
|
||||||
* populated by the MIB service itself. CTL_NET is expected to be populated
|
|
||||||
* through registration of remote subtrees. The libc sysctl(3) wrapper code
|
|
||||||
* takes care of the CTL_USER subtree. It must have an entry here though, or
|
|
||||||
* sysctl(8) will not list it. CTL_VENDOR is also empty, but writable, so that
|
|
||||||
* it may be used by third parties.
|
|
||||||
*/
|
|
||||||
static struct mib_node mib_table[] = {
|
|
||||||
/* 1*/ [CTL_KERN] = MIB_ENODE(_P | _RO, "kern", "High kernel"),
|
|
||||||
/* 2*/ [CTL_VM] = MIB_ENODE(_P | _RO, "vm", "Virtual memory"),
|
|
||||||
/* 4*/ [CTL_NET] = MIB_ENODE(_P | _RO, "net", "Networking"),
|
|
||||||
/* 6*/ [CTL_HW] = MIB_ENODE(_P | _RO, "hw", "Generic CPU, I/O"),
|
|
||||||
/* 8*/ [CTL_USER] = MIB_ENODE(_P | _RO, "user", "User-level"),
|
|
||||||
/*11*/ [CTL_VENDOR] = MIB_ENODE(_P | _RW, "vendor", "Vendor specific"),
|
|
||||||
/*32*/ [CTL_MINIX] = MIB_ENODE(_P | _RO, "minix", "MINIX3 specific"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The root node of the tree. The root node is used internally only--it is
|
|
||||||
* impossible to access the root node itself from userland in any way. The
|
|
||||||
* node is writable by default, so that programs such as init(8) may create
|
|
||||||
* their own top-level entries.
|
|
||||||
*/
|
|
||||||
struct mib_node mib_root = MIB_NODE(_RW, mib_table, "", "");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Structures describing old and new data as provided by userland. The primary
|
|
||||||
* advantage of these opaque structures is that we could in principle use them
|
|
||||||
* to implement storage of small data results in the sysctl reply message, so
|
|
||||||
* as to avoid the kernel copy, without changing any of the handler code.
|
|
||||||
*/
|
|
||||||
struct mib_oldp {
|
|
||||||
endpoint_t oldp_endpt;
|
|
||||||
vir_bytes oldp_addr;
|
|
||||||
size_t oldp_len;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* Same structure, different type: prevent accidental mixups, and avoid the
|
|
||||||
* need to use __restrict everywhere.
|
|
||||||
*/
|
|
||||||
struct mib_newp {
|
|
||||||
endpoint_t newp_endpt;
|
|
||||||
vir_bytes newp_addr;
|
|
||||||
size_t newp_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return TRUE or FALSE indicating whether the given offset is within the range
|
|
||||||
* of data that is to be copied out. This call can be used to test whether
|
|
||||||
* certain bits of data need to be prepared for copying at all.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_inrange(struct mib_oldp * oldp, size_t off)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (oldp == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return (off < oldp->oldp_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the total length of the requested data. This should not be used
|
|
||||||
* directly except in highly unusual cases, such as particular node requests
|
|
||||||
* where the request semantics blatantly violate overall sysctl(2) semantics.
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
mib_getoldlen(struct mib_oldp * oldp)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (oldp == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return oldp->oldp_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy out (partial) data to the user. The copy is automatically limited to
|
|
||||||
* the range of data requested by the user. Return the requested length on
|
|
||||||
* success (for the caller's convenience) or an error code on failure.
|
|
||||||
*/
|
|
||||||
ssize_t
|
|
||||||
mib_copyout(struct mib_oldp * __restrict oldp, size_t off,
|
|
||||||
const void * __restrict buf, size_t size)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
len = size;
|
|
||||||
assert(len <= SSIZE_MAX);
|
|
||||||
|
|
||||||
if (oldp == NULL || off >= oldp->oldp_len)
|
|
||||||
return size; /* nothing to do */
|
|
||||||
|
|
||||||
if (len > oldp->oldp_len - off)
|
|
||||||
len = oldp->oldp_len - off;
|
|
||||||
|
|
||||||
if ((r = sys_datacopy(SELF, (vir_bytes)buf, oldp->oldp_endpt,
|
|
||||||
oldp->oldp_addr + off, len)) != OK)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Override the oldlen value returned from the call, in situations where an
|
|
||||||
* error is thrown as well.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
mib_setoldlen(struct mib_call * call, size_t oldlen)
|
|
||||||
{
|
|
||||||
|
|
||||||
call->call_reslen = oldlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the new data length as provided by the user, or 0 if the user did not
|
|
||||||
* supply new data.
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
mib_getnewlen(struct mib_newp * newp)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (newp == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return newp->newp_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy in data from the user. The given length must match exactly the length
|
|
||||||
* given by the user. Return OK or an error code.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_copyin(struct mib_newp * __restrict newp, void * __restrict buf,
|
|
||||||
size_t len)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (newp == NULL || len != newp->newp_len)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
return OK;
|
|
||||||
|
|
||||||
return sys_datacopy(newp->newp_endpt, newp->newp_addr, SELF,
|
|
||||||
(vir_bytes)buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy in auxiliary data from the user, based on a user pointer obtained from
|
|
||||||
* data copied in earlier through mib_copyin().
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_copyin_aux(struct mib_newp * __restrict newp, vir_bytes addr,
|
|
||||||
void * __restrict buf, size_t len)
|
|
||||||
{
|
|
||||||
|
|
||||||
assert(newp != NULL);
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
return OK;
|
|
||||||
|
|
||||||
return sys_datacopy(newp->newp_endpt, addr, SELF, (vir_bytes)buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a grant for a call's old data region, if not NULL, for the given
|
|
||||||
* endpoint. On success, store the grant (or GRANT_INVALID) in grantp and the
|
|
||||||
* length in lenp, and return OK. On error, return an error code that must not
|
|
||||||
* be ENOMEM.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_relay_oldp(endpoint_t endpt, struct mib_oldp * __restrict oldp,
|
|
||||||
cp_grant_id_t * grantp, size_t * __restrict lenp)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (oldp != NULL) {
|
|
||||||
*grantp = cpf_grant_magic(endpt, oldp->oldp_endpt,
|
|
||||||
oldp->oldp_addr, oldp->oldp_len, CPF_WRITE);
|
|
||||||
if (!GRANT_VALID(*grantp))
|
|
||||||
return EINVAL;
|
|
||||||
*lenp = oldp->oldp_len;
|
|
||||||
} else {
|
|
||||||
*grantp = GRANT_INVALID;
|
|
||||||
*lenp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a grant for a call's new data region, if not NULL, for the given
|
|
||||||
* endpoint. On success, store the grant (or GRANT_INVALID) in grantp and the
|
|
||||||
* length in lenp, and return OK. On error, return an error code that must not
|
|
||||||
* be ENOMEM.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_relay_newp(endpoint_t endpt, struct mib_newp * __restrict newp,
|
|
||||||
cp_grant_id_t * grantp, size_t * __restrict lenp)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (newp != NULL) {
|
|
||||||
*grantp = cpf_grant_magic(endpt, newp->newp_endpt,
|
|
||||||
newp->newp_addr, newp->newp_len, CPF_READ);
|
|
||||||
if (!GRANT_VALID(*grantp))
|
|
||||||
return EINVAL;
|
|
||||||
*lenp = newp->newp_len;
|
|
||||||
} else {
|
|
||||||
*grantp = GRANT_INVALID;
|
|
||||||
*lenp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check whether the user is allowed to perform privileged operations. The
|
|
||||||
* function returns a nonzero value if this is the case, and zero otherwise.
|
|
||||||
* Authorization is performed only once per call.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
mib_authed(struct mib_call * call)
|
|
||||||
{
|
|
||||||
|
|
||||||
if ((call->call_flags & (MIB_FLAG_AUTH | MIB_FLAG_NOAUTH)) == 0) {
|
|
||||||
/* Ask PM if this endpoint has superuser privileges. */
|
|
||||||
if (getnuid(call->call_endpt) == SUPER_USER)
|
|
||||||
call->call_flags |= MIB_FLAG_AUTH;
|
|
||||||
else
|
|
||||||
call->call_flags |= MIB_FLAG_NOAUTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (call->call_flags & MIB_FLAG_AUTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implement the sysctl(2) system call.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mib_sysctl(message * __restrict m_in, int ipc_status,
|
|
||||||
message * __restrict m_out)
|
|
||||||
{
|
|
||||||
vir_bytes oldaddr, newaddr;
|
|
||||||
size_t oldlen, newlen;
|
|
||||||
unsigned int namelen;
|
|
||||||
int s, name[CTL_MAXNAME];
|
|
||||||
endpoint_t endpt;
|
|
||||||
struct mib_oldp oldp, *oldpp;
|
|
||||||
struct mib_newp newp, *newpp;
|
|
||||||
struct mib_call call;
|
|
||||||
ssize_t r;
|
|
||||||
|
|
||||||
/* Only handle blocking calls. Ignore everything else. */
|
|
||||||
if (IPC_STATUS_CALL(ipc_status) != SENDREC)
|
|
||||||
return EDONTREPLY;
|
|
||||||
|
|
||||||
endpt = m_in->m_source;
|
|
||||||
oldaddr = m_in->m_lc_mib_sysctl.oldp;
|
|
||||||
oldlen = m_in->m_lc_mib_sysctl.oldlen;
|
|
||||||
newaddr = m_in->m_lc_mib_sysctl.newp;
|
|
||||||
newlen = m_in->m_lc_mib_sysctl.newlen;
|
|
||||||
namelen = m_in->m_lc_mib_sysctl.namelen;
|
|
||||||
|
|
||||||
if (namelen == 0 || namelen > CTL_MAXNAME)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In most cases, the entire name fits in the request message, so we
|
|
||||||
* can avoid a kernel copy.
|
|
||||||
*/
|
|
||||||
if (namelen > CTL_SHORTNAME) {
|
|
||||||
if ((s = sys_datacopy(endpt, m_in->m_lc_mib_sysctl.namep, SELF,
|
|
||||||
(vir_bytes)&name, sizeof(name[0]) * namelen)) != OK)
|
|
||||||
return s;
|
|
||||||
} else
|
|
||||||
memcpy(name, m_in->m_lc_mib_sysctl.name,
|
|
||||||
sizeof(name[0]) * namelen);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up a structure for the old data, if any. When no old address is
|
|
||||||
* given, be forgiving if oldlen is not zero, as the user may simply
|
|
||||||
* not have initialized the variable before passing a pointer to it.
|
|
||||||
*/
|
|
||||||
if (oldaddr != 0) {
|
|
||||||
oldp.oldp_endpt = endpt;
|
|
||||||
oldp.oldp_addr = oldaddr;
|
|
||||||
oldp.oldp_len = oldlen;
|
|
||||||
oldpp = &oldp;
|
|
||||||
} else
|
|
||||||
oldpp = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up a structure for the new data, if any. If one of newaddr and
|
|
||||||
* newlen is zero but not the other, we (like NetBSD) disregard both.
|
|
||||||
*/
|
|
||||||
if (newaddr != 0 && newlen != 0) {
|
|
||||||
newp.newp_endpt = endpt;
|
|
||||||
newp.newp_addr = newaddr;
|
|
||||||
newp.newp_len = newlen;
|
|
||||||
newpp = &newp;
|
|
||||||
} else
|
|
||||||
newpp = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up a structure for other call parameters. Most of these should
|
|
||||||
* be used rarely, and we may want to add more later, so do not pass
|
|
||||||
* all of them around as actual function parameters all the time.
|
|
||||||
*/
|
|
||||||
call.call_endpt = endpt;
|
|
||||||
call.call_name = name;
|
|
||||||
call.call_namelen = namelen;
|
|
||||||
call.call_flags = 0;
|
|
||||||
call.call_reslen = 0;
|
|
||||||
|
|
||||||
r = mib_dispatch(&call, oldpp, newpp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* From NetBSD: we copy out as much as we can from the old data, while
|
|
||||||
* at the same time computing the full data length. Then, here at the
|
|
||||||
* end, if the entire result did not fit in the destination buffer, we
|
|
||||||
* return ENOMEM instead of success, thus also returning a partial
|
|
||||||
* result and the full data length.
|
|
||||||
*
|
|
||||||
* It is also possible that data are copied out along with a "real"
|
|
||||||
* error. In that case, we must report a nonzero resulting length
|
|
||||||
* along with that error code. This is currently the case when node
|
|
||||||
* creation resulted in a collision, in which case the error code is
|
|
||||||
* EEXIST while the existing node is copied out as well.
|
|
||||||
*/
|
|
||||||
if (r >= 0) {
|
|
||||||
m_out->m_mib_lc_sysctl.oldlen = (size_t)r;
|
|
||||||
|
|
||||||
if (oldaddr != 0 && oldlen < (size_t)r)
|
|
||||||
r = ENOMEM;
|
|
||||||
else
|
|
||||||
r = OK;
|
|
||||||
} else
|
|
||||||
m_out->m_mib_lc_sysctl.oldlen = call.call_reslen;
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the service.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
mib_init(int type __unused, sef_init_info_t * info __unused)
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize pointers and sizes of subtrees in different modules.
|
|
||||||
* This is needed because we cannot use sizeof on external arrays.
|
|
||||||
* We do initialize the node entry (including any other fields)
|
|
||||||
* statically through MIB_ENODE because that forces the array to be
|
|
||||||
* large enough to store the entry.
|
|
||||||
*/
|
|
||||||
mib_kern_init(&mib_table[CTL_KERN]);
|
|
||||||
mib_vm_init(&mib_table[CTL_VM]);
|
|
||||||
mib_hw_init(&mib_table[CTL_HW]);
|
|
||||||
mib_minix_init(&mib_table[CTL_MINIX]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now that the static tree is complete, go through the entire tree,
|
|
||||||
* initializing miscellaneous fields.
|
|
||||||
*/
|
|
||||||
mib_tree_init();
|
|
||||||
|
|
||||||
/* Prepare for requests to mount remote subtrees. */
|
|
||||||
mib_remote_init();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SEF startup.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
mib_startup(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
sef_setcb_init_fresh(mib_init);
|
|
||||||
/*
|
|
||||||
* If we restart we lose all dynamic state, which means we lose all
|
|
||||||
* nodes that have been created at run time. However, running with
|
|
||||||
* only the static node tree is still better than not running at all.
|
|
||||||
*/
|
|
||||||
sef_setcb_init_restart(mib_init);
|
|
||||||
|
|
||||||
sef_startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The Management Information Base (MIB) service.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
main(void)
|
|
||||||
{
|
|
||||||
message m_in, m_out;
|
|
||||||
int r, ipc_status;
|
|
||||||
|
|
||||||
/* Perform initialization. */
|
|
||||||
mib_startup();
|
|
||||||
|
|
||||||
/* The main message loop. */
|
|
||||||
for (;;) {
|
|
||||||
/* Receive a request. */
|
|
||||||
if ((r = sef_receive_status(ANY, &m_in, &ipc_status)) != OK)
|
|
||||||
panic("sef_receive failed: %d", r);
|
|
||||||
|
|
||||||
/* Process the request. */
|
|
||||||
if (is_ipc_notify(ipc_status)) {
|
|
||||||
/* We are not expecting any notifications. */
|
|
||||||
printf("MIB: notification from %d\n", m_in.m_source);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&m_out, 0, sizeof(m_out));
|
|
||||||
|
|
||||||
switch (m_in.m_type) {
|
|
||||||
case MIB_SYSCTL:
|
|
||||||
r = mib_sysctl(&m_in, ipc_status, &m_out);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MIB_REGISTER:
|
|
||||||
r = mib_register(&m_in, ipc_status);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MIB_DEREGISTER:
|
|
||||||
r = mib_deregister(&m_in, ipc_status);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (IPC_STATUS_CALL(ipc_status) == SENDREC)
|
|
||||||
r = ENOSYS;
|
|
||||||
else
|
|
||||||
r = EDONTREPLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send a reply, if applicable. */
|
|
||||||
if (r != EDONTREPLY) {
|
|
||||||
m_out.m_type = r;
|
|
||||||
|
|
||||||
if ((r = ipc_sendnb(m_in.m_source, &m_out)) != OK)
|
|
||||||
printf("MIB: ipc_sendnb failed (%d)\n", r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTREACHED */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,390 +0,0 @@
|
||||||
#ifndef _MINIX_MIB_MIB_H
|
|
||||||
#define _MINIX_MIB_MIB_H
|
|
||||||
|
|
||||||
#include <minix/drivers.h>
|
|
||||||
#include <minix/sysctl.h>
|
|
||||||
#include <machine/vmparam.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#if defined(__i386__)
|
|
||||||
#include "kernel/arch/i386/include/archconst.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_MAX_CPUS
|
|
||||||
#define CONFIG_MAX_CPUS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following setting toggles the existence of the minix.test subtree. For
|
|
||||||
* production environments, it should probably be disabled, although it should
|
|
||||||
* do no harm either. For development platforms, it should be enabled, or
|
|
||||||
* test87 will fail.
|
|
||||||
*/
|
|
||||||
#define MINIX_TEST_SUBTREE 1 /* include the minix.test subtree? */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By default, mount request failures will be silently discarded, because the
|
|
||||||
* requests themselves are one-way. For service authors, a bit more output may
|
|
||||||
* be helpful. Set the following defininition to "printf s" in order to
|
|
||||||
* include more information about mount requests and failures.
|
|
||||||
*/
|
|
||||||
#define MIB_DEBUG_MOUNT(s) /* printf s */
|
|
||||||
|
|
||||||
struct mib_oldp;
|
|
||||||
struct mib_newp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure contains a number of less heavily used parameters for handler
|
|
||||||
* functions, mainly to provide extensibility while limiting argument clutter.
|
|
||||||
*/
|
|
||||||
struct mib_call {
|
|
||||||
endpoint_t call_endpt; /* endpoint of the user process */
|
|
||||||
const int *call_name; /* remaining part of the name */
|
|
||||||
unsigned int call_namelen; /* length of the remaining name part */
|
|
||||||
unsigned int call_flags; /* internal call processing flags */
|
|
||||||
size_t call_reslen; /* resulting oldlen value on error */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Call flags. */
|
|
||||||
#define MIB_FLAG_AUTH 0x01 /* user verified to be superuser */
|
|
||||||
#define MIB_FLAG_NOAUTH 0x02 /* user verified to be regular user */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We reassign new meaning to three NetBSD node flags, because we do not use
|
|
||||||
* the flags in the way NetBSD does:
|
|
||||||
*
|
|
||||||
* - On NetBSD, CTLFLAG_ROOT is used to mark the root of the sysctl tree. The
|
|
||||||
* entire root node is not exposed to userland, and thus, neither is this
|
|
||||||
* flag. We do not need the flag as we do not have parent pointers.
|
|
||||||
* - On NetBSD, CTLFLAG_ALIAS is used to mark one node as an alias of another
|
|
||||||
* node, presumably to avoid having to duplicate entire subtrees. We can
|
|
||||||
* simply have two nodes point to the same subtree instead, and thus, we do
|
|
||||||
* not need to support this functionality at all.
|
|
||||||
* - On NetBSD, CTLFLAG_MMAP is defined for future support for memory-mapping
|
|
||||||
* node data with CTL_MMAP. It is not yet clear where or why this feature
|
|
||||||
* would be used in practice. For as long as NetBSD does not actually use
|
|
||||||
* this flag *for node-type nodes*, we can reuse it for our own purposes.
|
|
||||||
*
|
|
||||||
* The meaning of our replacement flags is explained further below. We ensure
|
|
||||||
* that none of these flags are ever exposed to userland. As such, our own
|
|
||||||
* definitions can be changed as necessary without breaking anything.
|
|
||||||
*/
|
|
||||||
#define CTLFLAG_PARENT CTLFLAG_ROOT /* node is a real parent node */
|
|
||||||
#define CTLFLAG_VERIFY CTLFLAG_ALIAS /* node has verification function */
|
|
||||||
#define CTLFLAG_REMOTE CTLFLAG_MMAP /* node is root of remote subtree */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following node structure definition aims to meet several goals at once:
|
|
||||||
*
|
|
||||||
* 1) it can be used for static and dynamic nodes;
|
|
||||||
* 2) it can be used to point to both static and dynamic child arrays at once;
|
|
||||||
* 3) it allows for embedded, pointed-to, and function-generated data;
|
|
||||||
* 4) it allows both temporary and obscuring mount points for remote subtrees;
|
|
||||||
* 5) its unions are compatible with magic instrumentation;
|
|
||||||
* 6) it is optimized for size, assuming many static and few dynamic nodes.
|
|
||||||
*
|
|
||||||
* All nodes have flags, a size, a version, a parent (except the root node), a
|
|
||||||
* name, and optionally a description. The use of the rest of the fields
|
|
||||||
* depends on the type of the node, which is defined as part of the node's
|
|
||||||
* flags field.
|
|
||||||
*
|
|
||||||
* Data nodes, that is, nodes of type CTLTYPE_{BOOL,INT,QUAD,STRING,STRUCT},
|
|
||||||
* have associated data. For types CTLTYPE_{BOOL,INT,QUAD}, the node may have
|
|
||||||
* immediate data (CTLFLAG_IMMEDIATE), in which case the value of the node is
|
|
||||||
* stored in the node structure itself (node_bool, node_int, node_quad). These
|
|
||||||
* node types may instead also have a pointer to data. This is always the case
|
|
||||||
* for types CTLTYPE_STRING and CTLTYPE_STRUCT. In that case, node_data is a
|
|
||||||
* valid pointer, and CTLFLAG_IMMEDIATE is not set. Either way, node_size is
|
|
||||||
* the size of the data, which for strings is the maximum string size; for
|
|
||||||
* other types, it defines the exact field size. In addition, data nodes may
|
|
||||||
* have the CTLFLAG_VERIFY flag set, which indicates that node_valid points
|
|
||||||
* to a callback function that verifies whether a newly written value is valid
|
|
||||||
* for the node. If this flag is not set, data nodes may have an associated
|
|
||||||
* function, in which case node_func is not NULL, which will be called to read
|
|
||||||
* and write data instead. The function may optionally use the node's regular
|
|
||||||
* (size, immediate and/or pointer) data fields as it sees fit.
|
|
||||||
*
|
|
||||||
* Node-type nodes, of type CTLTYPE_NODE, behave differently. Such nodes may
|
|
||||||
* have static and dynamic child nodes, or have an associated function, or be
|
|
||||||
* a mount point for a subtree handled by a remote process. The exact case is
|
|
||||||
* defined by the combination of the CTLFLAG_PARENT and CTLFLAG_REMOTE flags,
|
|
||||||
* yielding four possible cases:
|
|
||||||
*
|
|
||||||
* CTLFLAG_PARENT CTLFLAG_REMOTE Meaning
|
|
||||||
* not set not set The node has an associated function which
|
|
||||||
* handles all access to the entire subtree.
|
|
||||||
* set not set The node is the root of a real, local
|
|
||||||
* subtree with static and/or dynamic children.
|
|
||||||
* not set set The node is a temporarily created mount
|
|
||||||
* point for a remote tree. A remote service
|
|
||||||
* handles all access to the entire subtree.
|
|
||||||
* Unmounting the node also destroys the node.
|
|
||||||
* set set The node is a mount point that obscures a
|
|
||||||
* real, local subtree. A remote service
|
|
||||||
* handles all access to the entire subtree.
|
|
||||||
* Unmounting makes the original node visible.
|
|
||||||
*
|
|
||||||
* If the CTLFLAG_PARENT flag is set, the node is the root of a real sutree.
|
|
||||||
* For such nodes, node_size is the number (not size!) of the array of static
|
|
||||||
* child nodes, which is pointed to by node_scptr and indexed by child
|
|
||||||
* identifier. Within the static array, child nodes with zeroed flags fields
|
|
||||||
* are not in use. The node_dcptr field points to a linked list of dynamic
|
|
||||||
* child nodes. The node_csize field is set to the size of the static array
|
|
||||||
* plus the number of dynamic nodes; node_clen is set to the number of valid
|
|
||||||
* entries in the static array plus the number of dynamic nodes.
|
|
||||||
*
|
|
||||||
* If a function is set, and thus neither CTLFLAG_PARENT and CTLFLAG_REMOTE are
|
|
||||||
* set, none of the aforementioned fields are used, and the node_size field is
|
|
||||||
* typically (but not necessarily) set to zero.
|
|
||||||
*
|
|
||||||
* A remote service can mount its own subtree into the central MIB tree. The
|
|
||||||
* MIB service will then relay any requests for that subtree to the remote
|
|
||||||
* service. Both the mountpoint and the root of the remote subtree must be of
|
|
||||||
* type CTLTYPE_NODE; thus, no individual leaf nodes may be mounted. The mount
|
|
||||||
* point may either be created temporarily for the purpose of mounting (e.g.,
|
|
||||||
* net.inet), or it may override a preexisting node (e.g., kern.ipc). In the
|
|
||||||
* first case, the parent node must exist and be a node type (net). In the
|
|
||||||
* second case, the preexisting target node (the MIB service's kern.ipc) may
|
|
||||||
* not have an associated function and may only have static children. While
|
|
||||||
* being used as a mountpoint (i.e., have CTLFLAG_REMOTE set), the local node's
|
|
||||||
* node_csize and node_clen fields must not be used. Instead, the same space
|
|
||||||
* in the node structure is used to store information about the remote node:
|
|
||||||
* node_rid, node_tid, and the smaller node_rcsize and node_rclen which contain
|
|
||||||
* information about the root of the remote subtree. Remote nodes are also
|
|
||||||
* part of a linked list for administration purposes, using the node_next
|
|
||||||
* field. When a preexisting (CTLFLAG_PARENT) node is unmounted, its original
|
|
||||||
* node_csize and node_clen fields are recomputed.
|
|
||||||
*
|
|
||||||
* The structure uses unions for either only pointers or only non-pointers, to
|
|
||||||
* simplify live update support. However, this does not mean the structure is
|
|
||||||
* not fully used: real node-type nodes use node_{flags,size,ver,parent,csize,
|
|
||||||
* clen,scptr,dcptr,name,desc}, which together add up to the full structure
|
|
||||||
* size.
|
|
||||||
*/
|
|
||||||
struct mib_node;
|
|
||||||
struct mib_dynode;
|
|
||||||
|
|
||||||
typedef ssize_t (*mib_func_ptr)(struct mib_call *, struct mib_node *,
|
|
||||||
struct mib_oldp *, struct mib_newp *);
|
|
||||||
typedef int (*mib_verify_ptr)(struct mib_call *, struct mib_node *, void *,
|
|
||||||
size_t);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* To save space for the maintenance of remote nodes, we split up one uint32_t
|
|
||||||
* field into three subfields:
|
|
||||||
* - node_eid ("endpoint ID"), which is an index into the table of endpoints;
|
|
||||||
* - node_rcsize ("child size"), the number of child slots of the remote root;
|
|
||||||
* - node_rclen ("child length"), the number of children of the remote root.
|
|
||||||
* These fields impose limits on the number of endpoints known in the MIB
|
|
||||||
* service, and the maximum size of the remote subtree root.
|
|
||||||
*/
|
|
||||||
#define MIB_EID_BITS 5 /* up to 32 services can set remote subtrees */
|
|
||||||
#define MIB_RC_BITS 12 /* remote root may have up to 4096 children */
|
|
||||||
|
|
||||||
#if MIB_EID_BITS + 2 * MIB_RC_BITS > 32
|
|
||||||
#error "Sum of remote ID and remote children bit fields exceeds uint32_t size"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mib_node {
|
|
||||||
uint32_t node_flags; /* CTLTYPE_ type and CTLFLAG_ flags */
|
|
||||||
size_t node_size; /* size of associated data (bytes) */
|
|
||||||
uint32_t node_ver; /* node version */
|
|
||||||
struct mib_node *node_parent; /* pointer to parent node */
|
|
||||||
union ixfer_node_val_u {
|
|
||||||
struct {
|
|
||||||
uint32_t nvuc_csize; /* number of child slots */
|
|
||||||
uint32_t nvuc_clen; /* number of actual children */
|
|
||||||
} nvu_child;
|
|
||||||
struct {
|
|
||||||
uint32_t nvur_eid:MIB_EID_BITS; /* endpoint index */
|
|
||||||
uint32_t nvur_csize:MIB_RC_BITS;/* remote ch. slots */
|
|
||||||
uint32_t nvur_clen:MIB_RC_BITS; /* remote children */
|
|
||||||
uint32_t nvur_rid; /* opaque ID of remote root */
|
|
||||||
} nvu_remote;
|
|
||||||
bool nvu_bool; /* immediate boolean */
|
|
||||||
int nvu_int; /* immediate integer */
|
|
||||||
u_quad_t nvu_quad; /* immediate quad */
|
|
||||||
} node_val_u;
|
|
||||||
union pxfer_node_ptr_u {
|
|
||||||
void *npu_data; /* struct or string data pointer */
|
|
||||||
struct mib_node *npu_scptr; /* static child node array */
|
|
||||||
} node_ptr_u;
|
|
||||||
union pxfer_node_aux_u {
|
|
||||||
struct mib_dynode *nau_dcptr; /* dynamic child node list */
|
|
||||||
mib_func_ptr nau_func; /* handler function */
|
|
||||||
mib_verify_ptr nau_verify; /* verification function */
|
|
||||||
struct mib_node *nau_next; /* next remote node in list */
|
|
||||||
} node_aux_u;
|
|
||||||
const char *node_name; /* node name string */
|
|
||||||
const char *node_desc; /* node description (may be NULL) */
|
|
||||||
};
|
|
||||||
#define node_csize node_val_u.nvu_child.nvuc_csize
|
|
||||||
#define node_clen node_val_u.nvu_child.nvuc_clen
|
|
||||||
#define node_eid node_val_u.nvu_remote.nvur_eid
|
|
||||||
#define node_rcsize node_val_u.nvu_remote.nvur_csize
|
|
||||||
#define node_rclen node_val_u.nvu_remote.nvur_clen
|
|
||||||
#define node_rid node_val_u.nvu_remote.nvur_rid
|
|
||||||
#define node_bool node_val_u.nvu_bool
|
|
||||||
#define node_int node_val_u.nvu_int
|
|
||||||
#define node_quad node_val_u.nvu_quad
|
|
||||||
#define node_data node_ptr_u.npu_data
|
|
||||||
#define node_scptr node_ptr_u.npu_scptr
|
|
||||||
#define node_dcptr node_aux_u.nau_dcptr
|
|
||||||
#define node_func node_aux_u.nau_func
|
|
||||||
#define node_verify node_aux_u.nau_verify
|
|
||||||
#define node_next node_aux_u.nau_next
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure is used for dynamically allocated nodes, that is, nodes
|
|
||||||
* created by userland at run time. It contains not only the fields below, but
|
|
||||||
* also the full name and, for leaf nodes with non-immediate data, the actual
|
|
||||||
* data area, or, for temporary mount points for remote subtrees, the node's
|
|
||||||
* description.
|
|
||||||
*/
|
|
||||||
struct mib_dynode {
|
|
||||||
struct mib_dynode *dynode_next; /* next in linked dynamic node list */
|
|
||||||
int dynode_id; /* identifier of this node */
|
|
||||||
struct mib_node dynode_node; /* actual node */
|
|
||||||
char dynode_name[1]; /* node name data (variable size) */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Static node initialization macros. */
|
|
||||||
#define MIB_NODE(f,t,n,d) { \
|
|
||||||
.node_flags = CTLTYPE_NODE | CTLFLAG_PARENT | f, \
|
|
||||||
.node_size = __arraycount(t), \
|
|
||||||
.node_scptr = t, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_ENODE(f,n,d) { /* "E"mpty or "E"xternal */ \
|
|
||||||
.node_flags = CTLTYPE_NODE | CTLFLAG_PARENT | f, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_BOOL(f,b,n,d) { \
|
|
||||||
.node_flags = CTLTYPE_BOOL | CTLFLAG_IMMEDIATE | f, \
|
|
||||||
.node_size = sizeof(bool), \
|
|
||||||
.node_bool = b, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_INT(f,i,n,d) { \
|
|
||||||
.node_flags = CTLTYPE_INT | CTLFLAG_IMMEDIATE | f, \
|
|
||||||
.node_size = sizeof(int), \
|
|
||||||
.node_int = i, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_QUAD(f,q,n,d) { \
|
|
||||||
.node_flags = CTLTYPE_QUAD | CTLFLAG_IMMEDIATE | f, \
|
|
||||||
.node_size = sizeof(u_quad_t), \
|
|
||||||
.node_quad = q, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define _MIB_DATA(f,s,p,n,d) { \
|
|
||||||
.node_flags = f, \
|
|
||||||
.node_size = s, \
|
|
||||||
.node_data = __UNCONST(p), \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_BOOLPTR(f,p,n,d) _MIB_DATA(CTLTYPE_BOOL | f, sizeof(*p), p, n, d)
|
|
||||||
#define MIB_INTPTR(f,p,n,d) _MIB_DATA(CTLTYPE_INT | f, sizeof(*p), p, n, d)
|
|
||||||
#define MIB_QUADTR(f,p,n,d) _MIB_DATA(CTLTYPE_QUAD | f, sizeof(*p), p, n, d)
|
|
||||||
#define MIB_STRING(f,p,n,d) _MIB_DATA(CTLTYPE_STRING | f, sizeof(p), p, n, d)
|
|
||||||
#define MIB_STRUCT(f,s,p,n,d) _MIB_DATA(CTLTYPE_STRUCT | f, s, p, n, d)
|
|
||||||
#define MIB_FUNC(f,s,fp,n,d) { \
|
|
||||||
.node_flags = f, \
|
|
||||||
.node_size = s, \
|
|
||||||
.node_func = fp, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
#define MIB_INTV(f,i,vp,n,d) { \
|
|
||||||
.node_flags = CTLTYPE_INT | CTLFLAG_IMMEDIATE | \
|
|
||||||
CTLFLAG_VERIFY | f, \
|
|
||||||
.node_size = sizeof(int), \
|
|
||||||
.node_int = i, \
|
|
||||||
.node_verify = vp, \
|
|
||||||
.node_name = n, \
|
|
||||||
.node_desc = d \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finalize a node initialized with MIB_ENODE. */
|
|
||||||
#define MIB_INIT_ENODE(n,t) \
|
|
||||||
do { \
|
|
||||||
(n)->node_size = __arraycount(t); \
|
|
||||||
(n)->node_scptr = t; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* Some convenient shortcuts for highly common flags. */
|
|
||||||
#define _RO CTLFLAG_READONLY
|
|
||||||
#define _RW CTLFLAG_READWRITE
|
|
||||||
#define _P CTLFLAG_PERMANENT
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this check fails, all uses of "struct sysctlnode" and "struct sysctldesc"
|
|
||||||
* need to be revised, and translation between different versions of those
|
|
||||||
* structures may have to be added for backward compatibility.
|
|
||||||
*/
|
|
||||||
#if SYSCTL_VERSION != SYSCTL_VERS_1
|
|
||||||
#error "NetBSD sysctl headers are ahead of our implementation"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* main.c */
|
|
||||||
int mib_inrange(struct mib_oldp *, size_t);
|
|
||||||
size_t mib_getoldlen(struct mib_oldp *);
|
|
||||||
ssize_t mib_copyout(struct mib_oldp *, size_t, const void * __restrict,
|
|
||||||
size_t);
|
|
||||||
void mib_setoldlen(struct mib_call *, size_t);
|
|
||||||
size_t mib_getnewlen(struct mib_newp *);
|
|
||||||
int mib_copyin(struct mib_newp * __restrict, void * __restrict, size_t);
|
|
||||||
int mib_copyin_aux(struct mib_newp * __restrict, vir_bytes,
|
|
||||||
void * __restrict, size_t);
|
|
||||||
int mib_relay_oldp(endpoint_t, struct mib_oldp * __restrict, cp_grant_id_t *,
|
|
||||||
size_t * __restrict);
|
|
||||||
int mib_relay_newp(endpoint_t, struct mib_newp * __restrict, cp_grant_id_t *,
|
|
||||||
size_t * __restrict);
|
|
||||||
int mib_authed(struct mib_call *);
|
|
||||||
extern struct mib_node mib_root;
|
|
||||||
|
|
||||||
/* tree.c */
|
|
||||||
ssize_t mib_readwrite(struct mib_call *, struct mib_node *, struct mib_oldp *,
|
|
||||||
struct mib_newp *, mib_verify_ptr);
|
|
||||||
ssize_t mib_dispatch(struct mib_call *, struct mib_oldp *, struct mib_newp *);
|
|
||||||
void mib_tree_init(void);
|
|
||||||
int mib_mount(const int *, unsigned int, unsigned int, uint32_t, uint32_t,
|
|
||||||
unsigned int, unsigned int, struct mib_node **);
|
|
||||||
void mib_unmount(struct mib_node *);
|
|
||||||
extern unsigned int mib_nodes;
|
|
||||||
extern unsigned int mib_objects;
|
|
||||||
extern unsigned int mib_remotes;
|
|
||||||
|
|
||||||
/* remote.c */
|
|
||||||
void mib_remote_init(void);
|
|
||||||
int mib_register(const message *, int);
|
|
||||||
int mib_deregister(const message *, int);
|
|
||||||
int mib_remote_info(unsigned int, uint32_t, char *, size_t, char *, size_t);
|
|
||||||
ssize_t mib_remote_call(struct mib_call *, struct mib_node *,
|
|
||||||
struct mib_oldp *, struct mib_newp *);
|
|
||||||
|
|
||||||
/* proc.c */
|
|
||||||
ssize_t mib_kern_lwp(struct mib_call *, struct mib_node *, struct mib_oldp *,
|
|
||||||
struct mib_newp *);
|
|
||||||
ssize_t mib_kern_proc2(struct mib_call *, struct mib_node *, struct mib_oldp *,
|
|
||||||
struct mib_newp *);
|
|
||||||
ssize_t mib_kern_proc_args(struct mib_call *, struct mib_node *,
|
|
||||||
struct mib_oldp *, struct mib_newp *);
|
|
||||||
ssize_t mib_minix_proc_list(struct mib_call *, struct mib_node *,
|
|
||||||
struct mib_oldp *, struct mib_newp *);
|
|
||||||
ssize_t mib_minix_proc_data(struct mib_call *, struct mib_node *,
|
|
||||||
struct mib_oldp *, struct mib_newp *);
|
|
||||||
|
|
||||||
/* subtree modules */
|
|
||||||
void mib_kern_init(struct mib_node *);
|
|
||||||
void mib_vm_init(struct mib_node *);
|
|
||||||
void mib_hw_init(struct mib_node *);
|
|
||||||
void mib_minix_init(struct mib_node *);
|
|
||||||
|
|
||||||
#endif /* !_MINIX_MIB_MIB_H */
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,154 +0,0 @@
|
||||||
/* MIB service - vm.c - implementation of the CTL_VM subtree */
|
|
||||||
|
|
||||||
#include "mib.h"
|
|
||||||
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <uvm/uvm_extern.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_VM VM_LOADAVG.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_vm_loadavg(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
struct loadavg loadavg;
|
|
||||||
struct loadinfo loadinfo;
|
|
||||||
unsigned long proc_load;
|
|
||||||
u32_t ticks_per_slot, ticks;
|
|
||||||
unsigned int p;
|
|
||||||
int unfilled_ticks;
|
|
||||||
int h, slots, latest, slot;
|
|
||||||
int minutes[3] = { 1, 5, 15 };
|
|
||||||
|
|
||||||
assert(__arraycount(loadavg.ldavg) == __arraycount(minutes));
|
|
||||||
|
|
||||||
if (sys_getloadinfo(&loadinfo) != OK)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
memset(&loadavg, 0, sizeof(loadavg));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following code is inherited from the old MINIX libc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* How many ticks are missing from the newest-filled slot? */
|
|
||||||
ticks_per_slot = _LOAD_UNIT_SECS * sys_hz();
|
|
||||||
unfilled_ticks =
|
|
||||||
ticks_per_slot - (loadinfo.last_clock % ticks_per_slot);
|
|
||||||
|
|
||||||
for (p = 0; p < __arraycount(loadavg.ldavg); p++) {
|
|
||||||
latest = loadinfo.proc_last_slot;
|
|
||||||
slots = minutes[p] * 60 / _LOAD_UNIT_SECS;
|
|
||||||
proc_load = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add up the total number of process ticks for this number
|
|
||||||
* of minutes (minutes[p]). Start with the newest slot, which
|
|
||||||
* is latest, and count back for the number of slots that
|
|
||||||
* correspond to the right number of minutes. Take wraparound
|
|
||||||
* into account by calculating the index modulo _LOAD_HISTORY,
|
|
||||||
* which is the number of slots of history kept.
|
|
||||||
*/
|
|
||||||
for (h = 0; h < slots; h++) {
|
|
||||||
slot = (latest - h + _LOAD_HISTORY) % _LOAD_HISTORY;
|
|
||||||
proc_load += loadinfo.proc_load_history[slot];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The load average over this number of minutes is the number
|
|
||||||
* of process-ticks divided by the number of ticks, not
|
|
||||||
* counting the number of ticks the last slot hasn't been
|
|
||||||
* around yet.
|
|
||||||
*/
|
|
||||||
ticks = slots * ticks_per_slot - unfilled_ticks;
|
|
||||||
|
|
||||||
loadavg.ldavg[p] = 100UL * proc_load / ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadavg.fscale = 100L;
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &loadavg, sizeof(loadavg));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of CTL_VM VM_UVMEXP2.
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
mib_vm_uvmexp2(struct mib_call * call __unused,
|
|
||||||
struct mib_node * node __unused, struct mib_oldp * oldp,
|
|
||||||
struct mib_newp * newp __unused)
|
|
||||||
{
|
|
||||||
struct vm_stats_info vsi;
|
|
||||||
struct uvmexp_sysctl ues;
|
|
||||||
unsigned int shift;
|
|
||||||
|
|
||||||
if (vm_info_stats(&vsi) != OK)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
memset(&ues, 0, sizeof(ues));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: by far most of the structure is not filled correctly yet,
|
|
||||||
* since the MINIX3 system does not provide much of the information
|
|
||||||
* exposed by NetBSD. This will gradually have to be filled in.
|
|
||||||
* For now, we provide just some basic information used by top(1).
|
|
||||||
*/
|
|
||||||
ues.pagesize = vsi.vsi_pagesize;
|
|
||||||
ues.pagemask = vsi.vsi_pagesize - 1;
|
|
||||||
for (shift = 0; shift < CHAR_BIT * sizeof(void *); shift++)
|
|
||||||
if ((1U << shift) == vsi.vsi_pagesize)
|
|
||||||
break;
|
|
||||||
if (shift < CHAR_BIT * sizeof(void *))
|
|
||||||
ues.pageshift = shift;
|
|
||||||
ues.npages = vsi.vsi_total;
|
|
||||||
ues.free = vsi.vsi_free;
|
|
||||||
ues.filepages = vsi.vsi_cached;
|
|
||||||
/*
|
|
||||||
* We use one of the structure's unused fields to expose information
|
|
||||||
* not exposed by NetBSD, namely the largest area of physically
|
|
||||||
* contiguous memory. If NetBSD repurposes this field, we have to find
|
|
||||||
* another home for it (or expose it through a separate node or so).
|
|
||||||
*/
|
|
||||||
ues.unused1 = vsi.vsi_largest;
|
|
||||||
|
|
||||||
return mib_copyout(oldp, 0, &ues, sizeof(ues));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The CTL_VM nodes. */
|
|
||||||
static struct mib_node mib_vm_table[] = {
|
|
||||||
/* 1*/ /* VM_METER: not yet supported */
|
|
||||||
/* 2*/ [VM_LOADAVG] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
|
|
||||||
sizeof(struct loadavg), mib_vm_loadavg,
|
|
||||||
"loadavg", "System load average history"),
|
|
||||||
/* 3*/ /* VM_UVMEXP: not yet supported */
|
|
||||||
/* 4*/ /* VM_NKMEMPAGES: not yet supported */
|
|
||||||
/* 5*/ [VM_UVMEXP2] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
|
|
||||||
sizeof(struct uvmexp_sysctl),
|
|
||||||
mib_vm_uvmexp2, "uvmexp2",
|
|
||||||
"Detailed system-wide virtual memory "
|
|
||||||
"statistics (MI)"),
|
|
||||||
/* 6*/ /* VM_ANONMIN: not yet supported */
|
|
||||||
/* 7*/ /* VM_EXECMIN: not yet supported */
|
|
||||||
/* 8*/ /* VM_FILEMIN: not yet supported */
|
|
||||||
/* 9*/ [VM_MAXSLP] = MIB_INT(_P | _RO, MAXSLP, "maxslp",
|
|
||||||
"Maximum process sleep time before being "
|
|
||||||
"swapped"),
|
|
||||||
/*10*/ [VM_USPACE] = MIB_INT(_P | _RO, 0, "uspace", "Number of "
|
|
||||||
"bytes allocated for a kernel stack"),
|
|
||||||
/* MINIX3 processes don't have k-stacks */
|
|
||||||
/*11*/ /* VM_ANONMAX: not yet supported */
|
|
||||||
/*12*/ /* VM_EXECMAX: not yet supported */
|
|
||||||
/*13*/ /* VM_FILEMAX: not yet supported */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the CTL_VM subtree.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
mib_vm_init(struct mib_node * node)
|
|
||||||
{
|
|
||||||
|
|
||||||
MIB_INIT_ENODE(node, mib_vm_table);
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
# Makefile for Process Manager (PM)
|
|
||||||
PROG= pm
|
|
||||||
SRCS= main.c forkexit.c exec.c time.c alarm.c \
|
|
||||||
signal.c utility.c table.c trace.c getset.c misc.c \
|
|
||||||
profile.c mcontext.c schedule.c event.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS} ${LIBTIMERS}
|
|
||||||
LDADD+= -lsys -ltimers
|
|
||||||
|
|
||||||
CPPFLAGS.main.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS.misc.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS.schedule.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
CPPFLAGS.utility.c+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
/* This file handles the 6 system calls that get and set uids and gids.
|
|
||||||
* It also handles getpid(), setsid(), and getpgrp(). The code for each
|
|
||||||
* one is so tiny that it hardly seemed worthwhile to make each a separate
|
|
||||||
* function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pm.h"
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include <minix/endpoint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include "mproc.h"
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_get *
|
|
||||||
*===========================================================================*/
|
|
||||||
int
|
|
||||||
do_get(void)
|
|
||||||
{
|
|
||||||
/* Handle PM_GETUID, PM_GETGID, PM_GETGROUPS, PM_GETPID, PM_GETPGRP, PM_GETSID,
|
|
||||||
* PM_ISSETUGID.
|
|
||||||
*/
|
|
||||||
register struct mproc *rmp = mp;
|
|
||||||
int r;
|
|
||||||
int ngroups;
|
|
||||||
|
|
||||||
switch(call_nr) {
|
|
||||||
case PM_GETGROUPS:
|
|
||||||
ngroups = m_in.m_lc_pm_groups.num;
|
|
||||||
if (ngroups > NGROUPS_MAX || ngroups < 0)
|
|
||||||
return(EINVAL);
|
|
||||||
|
|
||||||
if (ngroups == 0) {
|
|
||||||
r = rmp->mp_ngroups;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngroups < rmp->mp_ngroups)
|
|
||||||
/* Asking for less groups than available */
|
|
||||||
return(EINVAL);
|
|
||||||
|
|
||||||
r = sys_datacopy(SELF, (vir_bytes) rmp->mp_sgroups, who_e,
|
|
||||||
m_in.m_lc_pm_groups.ptr, ngroups * sizeof(gid_t));
|
|
||||||
|
|
||||||
if (r != OK)
|
|
||||||
return(r);
|
|
||||||
|
|
||||||
r = rmp->mp_ngroups;
|
|
||||||
break;
|
|
||||||
case PM_GETUID:
|
|
||||||
r = rmp->mp_realuid;
|
|
||||||
rmp->mp_reply.m_pm_lc_getuid.euid = rmp->mp_effuid;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_GETGID:
|
|
||||||
r = rmp->mp_realgid;
|
|
||||||
rmp->mp_reply.m_pm_lc_getgid.egid = rmp->mp_effgid;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_GETPID:
|
|
||||||
r = mproc[who_p].mp_pid;
|
|
||||||
rmp->mp_reply.m_pm_lc_getpid.parent_pid = mproc[rmp->mp_parent].mp_pid;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_GETPGRP:
|
|
||||||
r = rmp->mp_procgrp;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_GETSID:
|
|
||||||
{
|
|
||||||
struct mproc *target;
|
|
||||||
pid_t p = m_in.m_lc_pm_getsid.pid;
|
|
||||||
target = p ? find_proc(p) : &mproc[who_p];
|
|
||||||
r = ESRCH;
|
|
||||||
if(target)
|
|
||||||
r = target->mp_procgrp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PM_ISSETUGID:
|
|
||||||
r = !!(rmp->mp_flags & TAINTED);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
r = EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_set *
|
|
||||||
*===========================================================================*/
|
|
||||||
int
|
|
||||||
do_set(void)
|
|
||||||
{
|
|
||||||
/* Handle PM_SETUID, PM_SETEUID, PM_SETGID, PM_SETGROUPS, PM_SETEGID, and
|
|
||||||
* SETSID. These calls have in common that, if successful, they will be
|
|
||||||
* forwarded to VFS as well.
|
|
||||||
*/
|
|
||||||
register struct mproc *rmp = mp;
|
|
||||||
message m;
|
|
||||||
int r, i;
|
|
||||||
int ngroups;
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
|
|
||||||
memset(&m, 0, sizeof(m));
|
|
||||||
|
|
||||||
switch(call_nr) {
|
|
||||||
case PM_SETUID:
|
|
||||||
uid = m_in.m_lc_pm_setuid.uid;
|
|
||||||
/* NetBSD specific semantics: setuid(geteuid()) may fail. */
|
|
||||||
if (rmp->mp_realuid != uid && rmp->mp_effuid != SUPER_USER)
|
|
||||||
return(EPERM);
|
|
||||||
/* BSD semantics: always update all three fields. */
|
|
||||||
rmp->mp_realuid = uid;
|
|
||||||
rmp->mp_effuid = uid;
|
|
||||||
rmp->mp_svuid = uid;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETUID;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
m.VFS_PM_EID = rmp->mp_effuid;
|
|
||||||
m.VFS_PM_RID = rmp->mp_realuid;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_SETEUID:
|
|
||||||
uid = m_in.m_lc_pm_setuid.uid;
|
|
||||||
/* BSD semantics: seteuid(geteuid()) may fail. */
|
|
||||||
if (rmp->mp_realuid != uid && rmp->mp_svuid != uid &&
|
|
||||||
rmp->mp_effuid != SUPER_USER)
|
|
||||||
return(EPERM);
|
|
||||||
rmp->mp_effuid = uid;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETUID;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
m.VFS_PM_EID = rmp->mp_effuid;
|
|
||||||
m.VFS_PM_RID = rmp->mp_realuid;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_SETGID:
|
|
||||||
gid = m_in.m_lc_pm_setgid.gid;
|
|
||||||
if (rmp->mp_realgid != gid && rmp->mp_effuid != SUPER_USER)
|
|
||||||
return(EPERM);
|
|
||||||
rmp->mp_realgid = gid;
|
|
||||||
rmp->mp_effgid = gid;
|
|
||||||
rmp->mp_svgid = gid;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETGID;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
m.VFS_PM_EID = rmp->mp_effgid;
|
|
||||||
m.VFS_PM_RID = rmp->mp_realgid;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_SETEGID:
|
|
||||||
gid = m_in.m_lc_pm_setgid.gid;
|
|
||||||
if (rmp->mp_realgid != gid && rmp->mp_svgid != gid &&
|
|
||||||
rmp->mp_effuid != SUPER_USER)
|
|
||||||
return(EPERM);
|
|
||||||
rmp->mp_effgid = gid;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETGID;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
m.VFS_PM_EID = rmp->mp_effgid;
|
|
||||||
m.VFS_PM_RID = rmp->mp_realgid;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PM_SETGROUPS:
|
|
||||||
if (rmp->mp_effuid != SUPER_USER)
|
|
||||||
return(EPERM);
|
|
||||||
|
|
||||||
ngroups = m_in.m_lc_pm_groups.num;
|
|
||||||
|
|
||||||
if (ngroups > NGROUPS_MAX || ngroups < 0)
|
|
||||||
return(EINVAL);
|
|
||||||
|
|
||||||
if (ngroups > 0 && m_in.m_lc_pm_groups.ptr == 0)
|
|
||||||
return(EFAULT);
|
|
||||||
|
|
||||||
r = sys_datacopy(who_e, m_in.m_lc_pm_groups.ptr, SELF,
|
|
||||||
(vir_bytes) rmp->mp_sgroups,
|
|
||||||
ngroups * sizeof(gid_t));
|
|
||||||
if (r != OK)
|
|
||||||
return(r);
|
|
||||||
|
|
||||||
for (i = 0; i < ngroups; i++) {
|
|
||||||
if (rmp->mp_sgroups[i] > GID_MAX)
|
|
||||||
return(EINVAL);
|
|
||||||
}
|
|
||||||
for (i = ngroups; i < NGROUPS_MAX; i++) {
|
|
||||||
rmp->mp_sgroups[i] = 0;
|
|
||||||
}
|
|
||||||
rmp->mp_ngroups = ngroups;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETGROUPS;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
m.VFS_PM_GROUP_NO = rmp->mp_ngroups;
|
|
||||||
m.VFS_PM_GROUP_ADDR = (char *) rmp->mp_sgroups;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PM_SETSID:
|
|
||||||
if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM);
|
|
||||||
rmp->mp_procgrp = rmp->mp_pid;
|
|
||||||
|
|
||||||
m.m_type = VFS_PM_SETSID;
|
|
||||||
m.VFS_PM_ENDPT = rmp->mp_endpoint;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the request to VFS */
|
|
||||||
tell_vfs(rmp, &m);
|
|
||||||
|
|
||||||
/* Do not reply until VFS has processed the request */
|
|
||||||
return(SUSPEND);
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
# Makefile for Reincarnation Server (RS)
|
|
||||||
PROG= rs
|
|
||||||
SRCS= exec.c main.c request.c manager.c table.c utility.c error.c update.c
|
|
||||||
|
|
||||||
.if ${USE_PCI} != "no"
|
|
||||||
CPPFLAGS+= -DUSE_PCI
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${USE_PCI} != "no"
|
|
||||||
CPPFLAGS+= -DUSE_PCI
|
|
||||||
.endif
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS} ${LIBEXEC}
|
|
||||||
LDADD+= -lsys -lexec
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,933 +0,0 @@
|
||||||
/* lookup() is the main routine that controls the path name lookup. It
|
|
||||||
* handles mountpoints and symbolic links. The actual lookup requests
|
|
||||||
* are sent through the req_lookup wrapper function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <minix/const.h>
|
|
||||||
#include <minix/endpoint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <minix/vfsif.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/dirent.h>
|
|
||||||
#include "vmnt.h"
|
|
||||||
#include "vnode.h"
|
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
/* Set to following define to 1 if you really want to use the POSIX definition
|
|
||||||
* (IEEE Std 1003.1, 2004) of pathname resolution. POSIX requires pathnames
|
|
||||||
* with a traling slash (and that do not entirely consist of slash characters)
|
|
||||||
* to be treated as if a single dot is appended. This means that for example
|
|
||||||
* mkdir("dir/", ...) and rmdir("dir/") will fail because the call tries to
|
|
||||||
* create or remove the directory '.'. Historically, Unix systems just ignore
|
|
||||||
* trailing slashes.
|
|
||||||
*/
|
|
||||||
#define DO_POSIX_PATHNAME_RES 0
|
|
||||||
|
|
||||||
static int lookup(struct vnode *dirp, struct lookup *resolve,
|
|
||||||
node_details_t *node, struct fproc *rfp);
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* advance *
|
|
||||||
*===========================================================================*/
|
|
||||||
struct vnode *
|
|
||||||
advance(struct vnode *dirp, struct lookup *resolve, struct fproc *rfp)
|
|
||||||
{
|
|
||||||
/* Resolve a path name starting at dirp to a vnode. */
|
|
||||||
int r;
|
|
||||||
int do_downgrade = 1;
|
|
||||||
struct vnode *new_vp, *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
struct node_details res = {0,0,0,0,0,0,0};
|
|
||||||
tll_access_t initial_locktype;
|
|
||||||
|
|
||||||
assert(dirp);
|
|
||||||
assert(resolve->l_vnode_lock != TLL_NONE);
|
|
||||||
assert(resolve->l_vmnt_lock != TLL_NONE);
|
|
||||||
|
|
||||||
if (resolve->l_vnode_lock == VNODE_READ)
|
|
||||||
initial_locktype = VNODE_OPCL;
|
|
||||||
else
|
|
||||||
initial_locktype = resolve->l_vnode_lock;
|
|
||||||
|
|
||||||
/* Get a free vnode and lock it */
|
|
||||||
if ((new_vp = get_free_vnode()) == NULL) return(NULL);
|
|
||||||
lock_vnode(new_vp, initial_locktype);
|
|
||||||
|
|
||||||
/* Lookup vnode belonging to the file. */
|
|
||||||
if ((r = lookup(dirp, resolve, &res, rfp)) != OK) {
|
|
||||||
err_code = r;
|
|
||||||
unlock_vnode(new_vp);
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether we already have a vnode for that file */
|
|
||||||
if ((vp = find_vnode(res.fs_e, res.inode_nr)) != NULL) {
|
|
||||||
unlock_vnode(new_vp); /* Don't need this anymore */
|
|
||||||
do_downgrade = (lock_vnode(vp, initial_locktype) != EBUSY);
|
|
||||||
|
|
||||||
/* Unfortunately, by the time we get the lock, another thread might've
|
|
||||||
* rid of the vnode (e.g., find_vnode found the vnode while a
|
|
||||||
* req_putnode was being processed). */
|
|
||||||
if (vp->v_ref_count == 0) { /* vnode vanished! */
|
|
||||||
/* As the lookup before increased the usage counters in the FS,
|
|
||||||
* we can simply set the usage counters to 1 and proceed as
|
|
||||||
* normal, because the putnode resulted in a use count of 1 in
|
|
||||||
* the FS. Other data is still valid, because the vnode was
|
|
||||||
* marked as pending lock, so get_free_vnode hasn't
|
|
||||||
* reinitialized the vnode yet. */
|
|
||||||
vp->v_fs_count = 1;
|
|
||||||
if (vp->v_mapfs_e != NONE) vp->v_mapfs_count = 1;
|
|
||||||
} else {
|
|
||||||
vp->v_fs_count++; /* We got a reference from the FS */
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Vnode not found, fill in the free vnode's fields */
|
|
||||||
|
|
||||||
new_vp->v_fs_e = res.fs_e;
|
|
||||||
new_vp->v_inode_nr = res.inode_nr;
|
|
||||||
new_vp->v_mode = res.fmode;
|
|
||||||
new_vp->v_size = res.fsize;
|
|
||||||
new_vp->v_uid = res.uid;
|
|
||||||
new_vp->v_gid = res.gid;
|
|
||||||
new_vp->v_sdev = res.dev;
|
|
||||||
|
|
||||||
if( (vmp = find_vmnt(new_vp->v_fs_e)) == NULL)
|
|
||||||
panic("advance: vmnt not found");
|
|
||||||
|
|
||||||
new_vp->v_vmnt = vmp;
|
|
||||||
new_vp->v_dev = vmp->m_dev;
|
|
||||||
new_vp->v_fs_count = 1;
|
|
||||||
|
|
||||||
vp = new_vp;
|
|
||||||
}
|
|
||||||
|
|
||||||
dup_vnode(vp);
|
|
||||||
if (do_downgrade) {
|
|
||||||
/* Only downgrade a lock if we managed to lock it in the first place */
|
|
||||||
*(resolve->l_vnode) = vp;
|
|
||||||
|
|
||||||
if (initial_locktype != resolve->l_vnode_lock)
|
|
||||||
tll_downgrade(&vp->v_lock);
|
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
|
||||||
if (resolve->l_vnode_lock == VNODE_READ)
|
|
||||||
fp->fp_vp_rdlocks++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return(vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* eat_path *
|
|
||||||
*===========================================================================*/
|
|
||||||
struct vnode *
|
|
||||||
eat_path(struct lookup *resolve, struct fproc *rfp)
|
|
||||||
{
|
|
||||||
/* Resolve path to a vnode. advance does the actual work. */
|
|
||||||
struct vnode *start_dir;
|
|
||||||
|
|
||||||
start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd : rfp->fp_wd);
|
|
||||||
return advance(start_dir, resolve, rfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* last_dir *
|
|
||||||
*===========================================================================*/
|
|
||||||
struct vnode *
|
|
||||||
last_dir(struct lookup *resolve, struct fproc *rfp)
|
|
||||||
{
|
|
||||||
/* Parse a path, as far as the last directory, fetch the vnode
|
|
||||||
* for the last directory into the vnode table, and return a pointer to the
|
|
||||||
* vnode. In addition, return the final component of the path in 'string'. If
|
|
||||||
* the last directory can't be opened, return NULL and the reason for
|
|
||||||
* failure in 'err_code'. We can't parse component by component as that would
|
|
||||||
* be too expensive. Alternatively, we cut off the last component of the path,
|
|
||||||
* and parse the path up to the penultimate component.
|
|
||||||
*/
|
|
||||||
|
|
||||||
size_t len;
|
|
||||||
char *cp;
|
|
||||||
char dir_entry[NAME_MAX+1];
|
|
||||||
struct vnode *start_dir, *res_vp, *sym_vp, *sym_vp_l, *loop_start;
|
|
||||||
struct vmnt *sym_vmp = NULL;
|
|
||||||
int r, symloop = 0, ret_on_symlink = 0;
|
|
||||||
struct lookup symlink;
|
|
||||||
|
|
||||||
*resolve->l_vnode = NULL;
|
|
||||||
*resolve->l_vmp = NULL;
|
|
||||||
loop_start = NULL;
|
|
||||||
sym_vp = NULL;
|
|
||||||
|
|
||||||
ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK);
|
|
||||||
|
|
||||||
do {
|
|
||||||
/* Is the path absolute or relative? Initialize 'start_dir'
|
|
||||||
* accordingly. Use loop_start in case we're looping.
|
|
||||||
*/
|
|
||||||
if (loop_start != NULL)
|
|
||||||
start_dir = loop_start;
|
|
||||||
else
|
|
||||||
start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd);
|
|
||||||
|
|
||||||
len = strlen(resolve->l_path);
|
|
||||||
|
|
||||||
/* If path is empty, return ENOENT. */
|
|
||||||
if (len == 0) {
|
|
||||||
err_code = ENOENT;
|
|
||||||
res_vp = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !DO_POSIX_PATHNAME_RES
|
|
||||||
/* Remove trailing slashes */
|
|
||||||
while (len > 1 && resolve->l_path[len-1] == '/') {
|
|
||||||
len--;
|
|
||||||
resolve->l_path[len]= '\0';
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cp = strrchr(resolve->l_path, '/');
|
|
||||||
if (cp == NULL) {
|
|
||||||
/* Just an entry in the current working directory. Prepend
|
|
||||||
* "./" in front of the path and resolve it.
|
|
||||||
*/
|
|
||||||
if (strlcpy(dir_entry, resolve->l_path, NAME_MAX+1) >= NAME_MAX + 1) {
|
|
||||||
err_code = ENAMETOOLONG;
|
|
||||||
res_vp = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dir_entry[NAME_MAX] = '\0';
|
|
||||||
resolve->l_path[0] = '.';
|
|
||||||
resolve->l_path[1] = '\0';
|
|
||||||
} else if (cp[1] == '\0') {
|
|
||||||
/* Path ends in a slash. The directory entry is '.' */
|
|
||||||
strlcpy(dir_entry, ".", NAME_MAX+1);
|
|
||||||
} else {
|
|
||||||
/* A path name for the directory and a directory entry */
|
|
||||||
if (strlcpy(dir_entry, cp+1, NAME_MAX+1) >= NAME_MAX + 1) {
|
|
||||||
err_code = ENAMETOOLONG;
|
|
||||||
res_vp = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cp[1] = '\0';
|
|
||||||
dir_entry[NAME_MAX] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove trailing slashes */
|
|
||||||
while (cp > resolve->l_path && cp[0] == '/') {
|
|
||||||
cp[0]= '\0';
|
|
||||||
cp--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resolve up to and including the last directory of the path. Turn off
|
|
||||||
* PATH_RET_SYMLINK, because we do want to follow the symlink in this
|
|
||||||
* case. That is, the flag is meant for the actual filename of the path,
|
|
||||||
* not the last directory.
|
|
||||||
*/
|
|
||||||
resolve->l_flags &= ~PATH_RET_SYMLINK;
|
|
||||||
if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the directory entry is not a symlink we're done now. If it is a
|
|
||||||
* symlink, then we're not at the last directory, yet. */
|
|
||||||
|
|
||||||
/* Copy the directory entry back to user_fullpath */
|
|
||||||
strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
|
|
||||||
|
|
||||||
/* Look up the directory entry, but do not follow the symlink when it
|
|
||||||
* is one. Note: depending on the previous advance, we might not be
|
|
||||||
* able to lock the resulting vnode. For example, when we look up "./."
|
|
||||||
* and request a VNODE_WRITE lock on the result, then the previous
|
|
||||||
* advance has "./" locked. The next advance to "." will try to lock
|
|
||||||
* the same vnode with a VNODE_READ lock, and fail. When that happens,
|
|
||||||
* sym_vp_l will be NULL and we must not unlock the vnode. If we would
|
|
||||||
* unlock, we actually unlock the vnode locked by the previous advance.
|
|
||||||
*/
|
|
||||||
lookup_init(&symlink, resolve->l_path,
|
|
||||||
resolve->l_flags|PATH_RET_SYMLINK, &sym_vmp, &sym_vp_l);
|
|
||||||
symlink.l_vmnt_lock = VMNT_READ;
|
|
||||||
symlink.l_vnode_lock = VNODE_READ;
|
|
||||||
sym_vp = advance(res_vp, &symlink, rfp);
|
|
||||||
|
|
||||||
if (sym_vp == NULL) break;
|
|
||||||
|
|
||||||
if (S_ISLNK(sym_vp->v_mode)) {
|
|
||||||
/* Last component is a symlink, but if we've been asked to not
|
|
||||||
* resolve it, return now.
|
|
||||||
*/
|
|
||||||
if (ret_on_symlink) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = req_rdlink(sym_vp->v_fs_e, sym_vp->v_inode_nr, NONE,
|
|
||||||
(vir_bytes) resolve->l_path, PATH_MAX - 1, 1);
|
|
||||||
|
|
||||||
if (r < 0) {
|
|
||||||
/* Failed to read link */
|
|
||||||
err_code = r;
|
|
||||||
unlock_vnode(res_vp);
|
|
||||||
unlock_vmnt(*resolve->l_vmp);
|
|
||||||
put_vnode(res_vp);
|
|
||||||
*resolve->l_vmp = NULL;
|
|
||||||
*resolve->l_vnode = NULL;
|
|
||||||
res_vp = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
resolve->l_path[r] = '\0';
|
|
||||||
|
|
||||||
if (strrchr(resolve->l_path, '/') != NULL) {
|
|
||||||
if (sym_vp_l != NULL)
|
|
||||||
unlock_vnode(sym_vp);
|
|
||||||
unlock_vmnt(*resolve->l_vmp);
|
|
||||||
if (sym_vmp != NULL)
|
|
||||||
unlock_vmnt(sym_vmp);
|
|
||||||
*resolve->l_vmp = NULL;
|
|
||||||
put_vnode(sym_vp);
|
|
||||||
sym_vp = NULL;
|
|
||||||
|
|
||||||
symloop++;
|
|
||||||
|
|
||||||
/* Relative symlinks are relative to res_vp, not cwd */
|
|
||||||
if (resolve->l_path[0] != '/') {
|
|
||||||
loop_start = res_vp;
|
|
||||||
} else {
|
|
||||||
/* Absolute symlink, forget about res_vp */
|
|
||||||
unlock_vnode(res_vp);
|
|
||||||
put_vnode(res_vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
symloop = 0; /* Not a symlink, so restart counting */
|
|
||||||
|
|
||||||
/* If we're crossing a mount point, return root node of mount
|
|
||||||
* point on which the file resides. That's the 'real' last
|
|
||||||
* dir that holds the file we're looking for.
|
|
||||||
*/
|
|
||||||
if (sym_vp->v_fs_e != res_vp->v_fs_e) {
|
|
||||||
assert(sym_vmp != NULL);
|
|
||||||
|
|
||||||
/* Unlock final file, it might have wrong lock types */
|
|
||||||
if (sym_vp_l != NULL)
|
|
||||||
unlock_vnode(sym_vp);
|
|
||||||
unlock_vmnt(sym_vmp);
|
|
||||||
put_vnode(sym_vp);
|
|
||||||
sym_vp = NULL;
|
|
||||||
|
|
||||||
/* Also unlock and release erroneous result */
|
|
||||||
unlock_vnode(*resolve->l_vnode);
|
|
||||||
unlock_vmnt(*resolve->l_vmp);
|
|
||||||
put_vnode(res_vp);
|
|
||||||
|
|
||||||
/* Relock vmnt and vnode with correct lock types */
|
|
||||||
lock_vmnt(sym_vmp, resolve->l_vmnt_lock);
|
|
||||||
lock_vnode(sym_vmp->m_root_node, resolve->l_vnode_lock);
|
|
||||||
res_vp = sym_vmp->m_root_node;
|
|
||||||
dup_vnode(res_vp);
|
|
||||||
*resolve->l_vnode = res_vp;
|
|
||||||
*resolve->l_vmp = sym_vmp;
|
|
||||||
|
|
||||||
/* We've effectively resolved the final component, so
|
|
||||||
* change it to current directory to prevent future
|
|
||||||
* 'advances' of returning erroneous results.
|
|
||||||
*/
|
|
||||||
strlcpy(dir_entry, ".", NAME_MAX+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} while (symloop < _POSIX_SYMLOOP_MAX);
|
|
||||||
|
|
||||||
if (symloop >= _POSIX_SYMLOOP_MAX) {
|
|
||||||
err_code = ELOOP;
|
|
||||||
res_vp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sym_vp != NULL) {
|
|
||||||
if (sym_vp_l != NULL) {
|
|
||||||
unlock_vnode(sym_vp);
|
|
||||||
}
|
|
||||||
if (sym_vmp != NULL) {
|
|
||||||
unlock_vmnt(sym_vmp);
|
|
||||||
}
|
|
||||||
put_vnode(sym_vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loop_start != NULL) {
|
|
||||||
unlock_vnode(loop_start);
|
|
||||||
put_vnode(loop_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the directory entry back to user_fullpath */
|
|
||||||
strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
|
|
||||||
|
|
||||||
/* Turn PATH_RET_SYMLINK flag back on if it was on */
|
|
||||||
if (ret_on_symlink) resolve->l_flags |= PATH_RET_SYMLINK;
|
|
||||||
|
|
||||||
return(res_vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* lookup *
|
|
||||||
*===========================================================================*/
|
|
||||||
static int
|
|
||||||
lookup(struct vnode *start_node, struct lookup *resolve, node_details_t *result_node, struct fproc *rfp)
|
|
||||||
{
|
|
||||||
/* Resolve a path name relative to start_node. */
|
|
||||||
|
|
||||||
int r, symloop;
|
|
||||||
endpoint_t fs_e;
|
|
||||||
size_t path_off, path_left_len;
|
|
||||||
ino_t dir_ino, root_ino;
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
struct vnode *dir_vp;
|
|
||||||
struct vmnt *vmp, *vmpres;
|
|
||||||
struct lookup_res res;
|
|
||||||
tll_access_t mnt_lock_type;
|
|
||||||
|
|
||||||
assert(resolve->l_vmp);
|
|
||||||
assert(resolve->l_vnode);
|
|
||||||
|
|
||||||
*(resolve->l_vmp) = vmpres = NULL; /* No vmnt found nor locked yet */
|
|
||||||
|
|
||||||
/* Empty (start) path? */
|
|
||||||
if (resolve->l_path[0] == '\0') {
|
|
||||||
result_node->inode_nr = 0;
|
|
||||||
return(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rfp->fp_rd || !rfp->fp_wd) {
|
|
||||||
printf("VFS: lookup %d: no rd/wd\n", rfp->fp_endpoint);
|
|
||||||
return(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs_e = start_node->v_fs_e;
|
|
||||||
dir_ino = start_node->v_inode_nr;
|
|
||||||
vmpres = find_vmnt(fs_e);
|
|
||||||
|
|
||||||
if (vmpres == NULL) return(EIO); /* mountpoint vanished? */
|
|
||||||
|
|
||||||
/* Is the process' root directory on the same partition?,
|
|
||||||
* if so, set the chroot directory too. */
|
|
||||||
if (rfp->fp_rd->v_dev == start_node->v_dev)
|
|
||||||
root_ino = rfp->fp_rd->v_inode_nr;
|
|
||||||
else
|
|
||||||
root_ino = 0;
|
|
||||||
|
|
||||||
/* Set user and group ids according to the system call */
|
|
||||||
uid = (job_call_nr == VFS_ACCESS ? rfp->fp_realuid : rfp->fp_effuid);
|
|
||||||
gid = (job_call_nr == VFS_ACCESS ? rfp->fp_realgid : rfp->fp_effgid);
|
|
||||||
|
|
||||||
symloop = 0; /* Number of symlinks seen so far */
|
|
||||||
|
|
||||||
/* Lock vmnt */
|
|
||||||
if (resolve->l_vmnt_lock == VMNT_READ)
|
|
||||||
mnt_lock_type = VMNT_WRITE;
|
|
||||||
else
|
|
||||||
mnt_lock_type = resolve->l_vmnt_lock;
|
|
||||||
|
|
||||||
if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
|
|
||||||
if (r == EBUSY) /* vmnt already locked */
|
|
||||||
vmpres = NULL;
|
|
||||||
else
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
*(resolve->l_vmp) = vmpres;
|
|
||||||
|
|
||||||
/* Issue the request */
|
|
||||||
r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
|
|
||||||
|
|
||||||
if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
*(resolve->l_vmp) = NULL;
|
|
||||||
return(r); /* i.e., an error occured */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* While the response is related to mount control set the
|
|
||||||
* new requests respectively */
|
|
||||||
while (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
|
|
||||||
/* Update user_fullpath to reflect what's left to be parsed. */
|
|
||||||
path_off = res.char_processed;
|
|
||||||
path_left_len = strlen(&resolve->l_path[path_off]);
|
|
||||||
memmove(resolve->l_path, &resolve->l_path[path_off], path_left_len);
|
|
||||||
resolve->l_path[path_left_len] = '\0'; /* terminate string */
|
|
||||||
|
|
||||||
/* Update the current value of the symloop counter */
|
|
||||||
symloop += res.symloop;
|
|
||||||
if (symloop > _POSIX_SYMLOOP_MAX) {
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
*(resolve->l_vmp) = NULL;
|
|
||||||
return(ELOOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Symlink encountered with absolute path */
|
|
||||||
if (r == ESYMLINK) {
|
|
||||||
dir_vp = rfp->fp_rd;
|
|
||||||
vmp = NULL;
|
|
||||||
} else if (r == EENTERMOUNT) {
|
|
||||||
/* Entering a new partition */
|
|
||||||
dir_vp = NULL;
|
|
||||||
/* Start node is now the mounted partition's root node */
|
|
||||||
for (vmp = &vmnt[0]; vmp != &vmnt[NR_MNTS]; ++vmp) {
|
|
||||||
if (vmp->m_dev != NO_DEV && vmp->m_mounted_on) {
|
|
||||||
if (vmp->m_mounted_on->v_inode_nr == res.inode_nr &&
|
|
||||||
vmp->m_mounted_on->v_fs_e == res.fs_e) {
|
|
||||||
dir_vp = vmp->m_root_node;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dir_vp == NULL) {
|
|
||||||
printf("VFS: path lookup error; root node not found\n");
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
*(resolve->l_vmp) = NULL;
|
|
||||||
return(EIO);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Climbing up mount */
|
|
||||||
/* Find the vmnt that represents the partition on
|
|
||||||
* which we "climb up". */
|
|
||||||
if ((vmp = find_vmnt(res.fs_e)) == NULL) {
|
|
||||||
panic("VFS lookup: can't find parent vmnt");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure that the child FS does not feed a bogus path
|
|
||||||
* to the parent FS. That is, when we climb up the tree, we
|
|
||||||
* must've encountered ".." in the path, and that is exactly
|
|
||||||
* what we're going to feed to the parent */
|
|
||||||
if(strncmp(resolve->l_path, "..", 2) != 0 ||
|
|
||||||
(resolve->l_path[2] != '\0' && resolve->l_path[2] != '/')) {
|
|
||||||
printf("VFS: bogus path: %s\n", resolve->l_path);
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
*(resolve->l_vmp) = NULL;
|
|
||||||
return(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start node is the vnode on which the partition is
|
|
||||||
* mounted */
|
|
||||||
dir_vp = vmp->m_mounted_on;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the starting directories inode number and FS endpoint */
|
|
||||||
fs_e = dir_vp->v_fs_e;
|
|
||||||
dir_ino = dir_vp->v_inode_nr;
|
|
||||||
|
|
||||||
/* Is the process' root directory on the same partition?,
|
|
||||||
* if so, set the chroot directory too. */
|
|
||||||
if (dir_vp->v_dev == rfp->fp_rd->v_dev)
|
|
||||||
root_ino = rfp->fp_rd->v_inode_nr;
|
|
||||||
else
|
|
||||||
root_ino = 0;
|
|
||||||
|
|
||||||
/* Unlock a previously locked vmnt if locked and lock new vmnt */
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
vmpres = find_vmnt(fs_e);
|
|
||||||
if (vmpres == NULL) return(EIO); /* mount point vanished? */
|
|
||||||
if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
|
|
||||||
if (r == EBUSY)
|
|
||||||
vmpres = NULL; /* Already locked */
|
|
||||||
else
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
*(resolve->l_vmp) = vmpres;
|
|
||||||
|
|
||||||
r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
|
|
||||||
|
|
||||||
if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
|
|
||||||
if (vmpres) unlock_vmnt(vmpres);
|
|
||||||
*(resolve->l_vmp) = NULL;
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*(resolve->l_vmp) != NULL && resolve->l_vmnt_lock != mnt_lock_type) {
|
|
||||||
/* downgrade VMNT_WRITE to VMNT_READ */
|
|
||||||
downgrade_vmnt_lock(*(resolve->l_vmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill in response fields */
|
|
||||||
result_node->inode_nr = res.inode_nr;
|
|
||||||
result_node->fmode = res.fmode;
|
|
||||||
result_node->fsize = res.fsize;
|
|
||||||
result_node->dev = res.dev;
|
|
||||||
result_node->fs_e = res.fs_e;
|
|
||||||
result_node->uid = res.uid;
|
|
||||||
result_node->gid = res.gid;
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* lookup_init *
|
|
||||||
*===========================================================================*/
|
|
||||||
void
|
|
||||||
lookup_init(struct lookup *resolve, char *path, int flags, struct vmnt **vmp, struct vnode **vp)
|
|
||||||
{
|
|
||||||
assert(vmp != NULL);
|
|
||||||
assert(vp != NULL);
|
|
||||||
|
|
||||||
resolve->l_path = path;
|
|
||||||
resolve->l_flags = flags;
|
|
||||||
resolve->l_vmp = vmp;
|
|
||||||
resolve->l_vnode = vp;
|
|
||||||
resolve->l_vmnt_lock = TLL_NONE;
|
|
||||||
resolve->l_vnode_lock = TLL_NONE;
|
|
||||||
*vmp = NULL; /* Initialize lookup result to NULL */
|
|
||||||
*vp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* get_name *
|
|
||||||
*===========================================================================*/
|
|
||||||
int
|
|
||||||
get_name(struct vnode *dirp, struct vnode *entry, char ename[NAME_MAX + 1])
|
|
||||||
{
|
|
||||||
#define DIR_ENTRIES 8
|
|
||||||
#define DIR_ENTRY_SIZE (sizeof(struct dirent) + NAME_MAX)
|
|
||||||
off_t pos, new_pos;
|
|
||||||
int r, consumed, totalbytes, name_len;
|
|
||||||
char buf[DIR_ENTRY_SIZE * DIR_ENTRIES];
|
|
||||||
struct dirent *cur;
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
|
|
||||||
if (!S_ISDIR(dirp->v_mode)) return(EBADF);
|
|
||||||
|
|
||||||
do {
|
|
||||||
r = req_getdents(dirp->v_fs_e, dirp->v_inode_nr, pos, (vir_bytes)buf,
|
|
||||||
sizeof(buf), &new_pos, 1);
|
|
||||||
|
|
||||||
if (r == 0) {
|
|
||||||
return(ENOENT); /* end of entries -- matching inode !found */
|
|
||||||
} else if (r < 0) {
|
|
||||||
return(r); /* error */
|
|
||||||
}
|
|
||||||
|
|
||||||
consumed = 0; /* bytes consumed */
|
|
||||||
totalbytes = r; /* number of bytes to consume */
|
|
||||||
|
|
||||||
do {
|
|
||||||
cur = (struct dirent *) (buf + consumed);
|
|
||||||
name_len = cur->d_reclen - offsetof(struct dirent, d_name) - 1;
|
|
||||||
|
|
||||||
if(cur->d_name + name_len+1 > &buf[sizeof(buf)])
|
|
||||||
return(EINVAL); /* Rubbish in dir entry */
|
|
||||||
if (entry->v_inode_nr == cur->d_fileno) {
|
|
||||||
/* found the entry we were looking for */
|
|
||||||
int copylen = MIN(name_len + 1, NAME_MAX + 1);
|
|
||||||
if (strlcpy(ename, cur->d_name, copylen) >= copylen) {
|
|
||||||
return(ENAMETOOLONG);
|
|
||||||
}
|
|
||||||
ename[NAME_MAX] = '\0';
|
|
||||||
return(OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not a match -- move on to the next dirent */
|
|
||||||
consumed += cur->d_reclen;
|
|
||||||
} while (consumed < totalbytes);
|
|
||||||
|
|
||||||
pos = new_pos;
|
|
||||||
} while (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* canonical_path *
|
|
||||||
*===========================================================================*/
|
|
||||||
int
|
|
||||||
canonical_path(char orig_path[PATH_MAX], struct fproc *rfp)
|
|
||||||
{
|
|
||||||
/* Find canonical path of a given path */
|
|
||||||
int len = 0;
|
|
||||||
int r, symloop = 0;
|
|
||||||
struct vnode *dir_vp, *parent_dir;
|
|
||||||
struct vmnt *dir_vmp, *parent_vmp;
|
|
||||||
char component[NAME_MAX+1]; /* NAME_MAX does /not/ include '\0' */
|
|
||||||
char temp_path[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
|
|
||||||
parent_dir = dir_vp = NULL;
|
|
||||||
parent_vmp = dir_vmp = NULL;
|
|
||||||
strlcpy(temp_path, orig_path, PATH_MAX);
|
|
||||||
temp_path[PATH_MAX - 1] = '\0';
|
|
||||||
|
|
||||||
/* First resolve path to the last directory holding the file */
|
|
||||||
do {
|
|
||||||
if (dir_vp) {
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup_init(&resolve, temp_path, PATH_NOFLAGS, &dir_vmp, &dir_vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
if ((dir_vp = last_dir(&resolve, rfp)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
/* dir_vp points to dir and resolve path now contains only the
|
|
||||||
* filename.
|
|
||||||
*/
|
|
||||||
strlcpy(orig_path, temp_path, NAME_MAX+1); /* Store file name */
|
|
||||||
|
|
||||||
/* If we're just crossing a mount point, our name has changed to '.' */
|
|
||||||
if (!strcmp(orig_path, ".")) orig_path[0] = '\0';
|
|
||||||
|
|
||||||
/* check if the file is a symlink, if so resolve it */
|
|
||||||
r = rdlink_direct(orig_path, temp_path, rfp);
|
|
||||||
|
|
||||||
if (r <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* encountered a symlink -- loop again */
|
|
||||||
strlcpy(orig_path, temp_path, PATH_MAX);
|
|
||||||
symloop++;
|
|
||||||
} while (symloop < _POSIX_SYMLOOP_MAX);
|
|
||||||
|
|
||||||
if (symloop >= _POSIX_SYMLOOP_MAX) {
|
|
||||||
if (dir_vp) {
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
}
|
|
||||||
return(ELOOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We've got the filename and the actual directory holding the file. From
|
|
||||||
* here we start building up the canonical path by climbing up the tree */
|
|
||||||
while (dir_vp != rfp->fp_rd) {
|
|
||||||
|
|
||||||
strlcpy(temp_path, "..", NAME_MAX+1);
|
|
||||||
|
|
||||||
/* check if we're at the root node of the file system */
|
|
||||||
if (dir_vp->v_vmnt->m_root_node == dir_vp) {
|
|
||||||
if (dir_vp->v_vmnt->m_mounted_on == NULL) {
|
|
||||||
/* Bail out, we can't go any higher */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
dir_vp = dir_vp->v_vmnt->m_mounted_on;
|
|
||||||
dir_vmp = dir_vp->v_vmnt;
|
|
||||||
if (lock_vmnt(dir_vmp, VMNT_READ) != OK)
|
|
||||||
panic("failed to lock vmnt");
|
|
||||||
if (lock_vnode(dir_vp, VNODE_READ) != OK)
|
|
||||||
panic("failed to lock vnode");
|
|
||||||
dup_vnode(dir_vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup_init(&resolve, temp_path, PATH_NOFLAGS, &parent_vmp,
|
|
||||||
&parent_dir);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
|
|
||||||
if ((parent_dir = advance(dir_vp, &resolve, rfp)) == NULL) {
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
return(err_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now we have to retrieve the name of the parent directory */
|
|
||||||
if ((r = get_name(parent_dir, dir_vp, component)) != OK) {
|
|
||||||
unlock_vnode(parent_dir);
|
|
||||||
unlock_vmnt(parent_vmp);
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(parent_dir);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
len += strlen(component) + 1;
|
|
||||||
if (len >= PATH_MAX) {
|
|
||||||
/* adding the component to orig_path would exceed PATH_MAX */
|
|
||||||
unlock_vnode(parent_dir);
|
|
||||||
unlock_vmnt(parent_vmp);
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(parent_dir);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
return(ENOMEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store result of component in orig_path. First make space by moving
|
|
||||||
* the contents of orig_path to the right. Move strlen + 1 bytes to
|
|
||||||
* include the terminating '\0'. Move to strlen + 1 bytes to reserve
|
|
||||||
* space for the slash.
|
|
||||||
*/
|
|
||||||
memmove(orig_path+strlen(component)+1, orig_path, strlen(orig_path)+1);
|
|
||||||
/* Copy component into canon_path */
|
|
||||||
memmove(orig_path, component, strlen(component));
|
|
||||||
/* Put slash into place */
|
|
||||||
orig_path[strlen(component)] = '/';
|
|
||||||
|
|
||||||
/* Store parent_dir result, and continue the loop once more */
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
dir_vp = parent_dir;
|
|
||||||
dir_vmp = parent_vmp;
|
|
||||||
parent_vmp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
unlock_vnode(dir_vp);
|
|
||||||
put_vnode(dir_vp);
|
|
||||||
|
|
||||||
/* add the leading slash */
|
|
||||||
len = strlen(orig_path);
|
|
||||||
if (strlen(orig_path) >= PATH_MAX) return(ENAMETOOLONG);
|
|
||||||
memmove(orig_path+1, orig_path, len + 1 /* include terminating nul */);
|
|
||||||
orig_path[0] = '/';
|
|
||||||
|
|
||||||
/* remove trailing slash if there is any */
|
|
||||||
if (len > 1 && orig_path[len] == '/') orig_path[len] = '\0';
|
|
||||||
|
|
||||||
return(OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_socketpath *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_socketpath(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Perform a path action on an on-disk socket file. This call may be performed
|
|
||||||
* by the UDS service only. The action is always on behalf of a user process
|
|
||||||
* that is currently making a socket call to the UDS service, and thus, VFS may
|
|
||||||
* rely on the fact that the user process is blocked. TODO: there should be
|
|
||||||
* checks in place to prevent (even accidental) abuse of this function, though.
|
|
||||||
*/
|
|
||||||
int r, what, slot;
|
|
||||||
endpoint_t ep;
|
|
||||||
cp_grant_id_t io_gr;
|
|
||||||
size_t pathlen;
|
|
||||||
struct vnode *dirp, *vp;
|
|
||||||
struct vmnt *vmp, *vmp2;
|
|
||||||
struct fproc *rfp;
|
|
||||||
char path[PATH_MAX];
|
|
||||||
struct lookup resolve, resolve2;
|
|
||||||
mode_t bits;
|
|
||||||
|
|
||||||
/* This should be replaced by an ACL check. */
|
|
||||||
if (!super_user) return EPERM;
|
|
||||||
|
|
||||||
ep = job_m_in.m_lsys_vfs_socketpath.endpt;
|
|
||||||
io_gr = job_m_in.m_lsys_vfs_socketpath.grant;
|
|
||||||
pathlen = job_m_in.m_lsys_vfs_socketpath.count;
|
|
||||||
what = job_m_in.m_lsys_vfs_socketpath.what;
|
|
||||||
|
|
||||||
if (isokendpt(ep, &slot) != OK) return(EINVAL);
|
|
||||||
rfp = &fproc[slot];
|
|
||||||
|
|
||||||
/* Copy in the path name, which must not be empty. It is typically not null
|
|
||||||
* terminated.
|
|
||||||
*/
|
|
||||||
if (pathlen < 1 || pathlen >= sizeof(path)) return(EINVAL);
|
|
||||||
r = sys_safecopyfrom(who_e, io_gr, (vir_bytes)0, (vir_bytes)path, pathlen);
|
|
||||||
if (r != OK) return(r);
|
|
||||||
path[pathlen] = '\0';
|
|
||||||
|
|
||||||
/* Now perform the requested action. For the SPATH_CHECK action, a socket
|
|
||||||
* file is expected to exist already, and we should check whether the given
|
|
||||||
* user process has access to it. For the SPATH_CREATE action, no file is
|
|
||||||
* expected to exist yet, and a socket file should be created on behalf of
|
|
||||||
* the user process. In both cases, on success, return the socket file's
|
|
||||||
* device and inode numbers to the caller.
|
|
||||||
*
|
|
||||||
* Since the above canonicalization releases all locks once done, we need to
|
|
||||||
* recheck absolutely everything now. TODO: do not release locks in between.
|
|
||||||
*/
|
|
||||||
switch (what) {
|
|
||||||
case SPATH_CHECK:
|
|
||||||
lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
/* Check file type and permissions. */
|
|
||||||
if (!S_ISSOCK(vp->v_mode))
|
|
||||||
r = ENOTSOCK; /* not in POSIX spec; this is what NetBSD does */
|
|
||||||
else
|
|
||||||
r = forbidden(rfp, vp, R_BIT | W_BIT);
|
|
||||||
|
|
||||||
if (r == OK) {
|
|
||||||
job_m_out.m_vfs_lsys_socketpath.device = vp->v_dev;
|
|
||||||
job_m_out.m_vfs_lsys_socketpath.inode = vp->v_inode_nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
put_vnode(vp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SPATH_CREATE:
|
|
||||||
/* This is effectively simulating a mknod(2) call by the user process,
|
|
||||||
* including the application of its umask to the file permissions.
|
|
||||||
*/
|
|
||||||
lookup_init(&resolve, path, PATH_RET_SYMLINK, &vmp, &dirp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_WRITE;
|
|
||||||
resolve.l_vnode_lock = VNODE_WRITE;
|
|
||||||
|
|
||||||
if ((dirp = last_dir(&resolve, rfp)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
bits = S_IFSOCK | (ACCESSPERMS & rfp->fp_umask);
|
|
||||||
|
|
||||||
if (!S_ISDIR(dirp->v_mode))
|
|
||||||
r = ENOTDIR;
|
|
||||||
else if ((r = forbidden(rfp, dirp, W_BIT | X_BIT)) == OK) {
|
|
||||||
r = req_mknod(dirp->v_fs_e, dirp->v_inode_nr, path,
|
|
||||||
rfp->fp_effuid, rfp->fp_effgid, bits, NO_DEV);
|
|
||||||
if (r == OK) {
|
|
||||||
/* Now we need to find out the device and inode number
|
|
||||||
* of the socket file we just created. The vmnt lock
|
|
||||||
* should prevent any trouble here.
|
|
||||||
*/
|
|
||||||
lookup_init(&resolve2, resolve.l_path,
|
|
||||||
PATH_RET_SYMLINK, &vmp2, &vp);
|
|
||||||
resolve2.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve2.l_vnode_lock = VNODE_READ;
|
|
||||||
vp = advance(dirp, &resolve2, rfp);
|
|
||||||
assert(vmp2 == NULL);
|
|
||||||
if (vp != NULL) {
|
|
||||||
job_m_out.m_vfs_lsys_socketpath.device =
|
|
||||||
vp->v_dev;
|
|
||||||
job_m_out.m_vfs_lsys_socketpath.inode =
|
|
||||||
vp->v_inode_nr;
|
|
||||||
unlock_vnode(vp);
|
|
||||||
put_vnode(vp);
|
|
||||||
} else {
|
|
||||||
/* Huh. This should never happen. If it does,
|
|
||||||
* we assume the socket file has somehow been
|
|
||||||
* lost, so we do not try to unlink it.
|
|
||||||
*/
|
|
||||||
printf("VFS: socketpath did not find created "
|
|
||||||
"node at %s (%d)\n", path, err_code);
|
|
||||||
r = err_code;
|
|
||||||
}
|
|
||||||
} else if (r == EEXIST)
|
|
||||||
r = EADDRINUSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock_vnode(dirp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
put_vnode(dirp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
r = ENOSYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
@ -1,446 +0,0 @@
|
||||||
/* This file contains the code for performing four system calls relating to
|
|
||||||
* status and directories.
|
|
||||||
*
|
|
||||||
* The entry points into this file are
|
|
||||||
* do_chdir: perform the CHDIR system call
|
|
||||||
* do_chroot: perform the CHROOT system call
|
|
||||||
* do_lstat: perform the LSTAT system call
|
|
||||||
* do_stat: perform the STAT system call
|
|
||||||
* do_fstat: perform the FSTAT system call
|
|
||||||
* do_statvfs: perform the STATVFS1 system call
|
|
||||||
* do_fstatvfs: perform the FSTATVFS1 system call
|
|
||||||
* do_getvfsstat: perform the GETVFSSTAT system call
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <minix/com.h>
|
|
||||||
#include <minix/u64.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "file.h"
|
|
||||||
#include "path.h"
|
|
||||||
#include <minix/vfsif.h>
|
|
||||||
#include <minix/callnr.h>
|
|
||||||
#include "vnode.h"
|
|
||||||
#include "vmnt.h"
|
|
||||||
|
|
||||||
static int change_into(struct vnode **iip, struct vnode *vp);
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_fchdir *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_fchdir(void)
|
|
||||||
{
|
|
||||||
/* Change directory on already-opened fd. */
|
|
||||||
struct filp *rfilp;
|
|
||||||
int r, rfd;
|
|
||||||
|
|
||||||
rfd = job_m_in.m_lc_vfs_fchdir.fd;
|
|
||||||
|
|
||||||
/* Is the file descriptor valid? */
|
|
||||||
if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
|
|
||||||
r = change_into(&fp->fp_wd, rfilp->filp_vno);
|
|
||||||
unlock_filp(rfilp);
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_chdir *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_chdir(void)
|
|
||||||
{
|
|
||||||
/* Perform the chdir(name) system call.
|
|
||||||
* syscall might provide 'name' embedded in the message.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int r;
|
|
||||||
struct vnode *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
char fullpath[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
|
|
||||||
if (copy_path(fullpath, sizeof(fullpath)) != OK)
|
|
||||||
return(err_code);
|
|
||||||
|
|
||||||
/* Try to open the directory */
|
|
||||||
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
r = change_into(&fp->fp_wd, vp);
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
put_vnode(vp);
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_chroot *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_chroot(void)
|
|
||||||
{
|
|
||||||
/* Perform the chroot(name) system call.
|
|
||||||
* syscall might provide 'name' embedded in the message.
|
|
||||||
*/
|
|
||||||
int r;
|
|
||||||
struct vnode *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
char fullpath[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
|
|
||||||
if (!super_user) return(EPERM); /* only su may chroot() */
|
|
||||||
|
|
||||||
if (copy_path(fullpath, sizeof(fullpath)) != OK)
|
|
||||||
return(err_code);
|
|
||||||
|
|
||||||
/* Try to open the directory */
|
|
||||||
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
r = change_into(&fp->fp_rd, vp);
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
put_vnode(vp);
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* change_into *
|
|
||||||
*===========================================================================*/
|
|
||||||
static int change_into(struct vnode **result, struct vnode *vp)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (*result == vp) return(OK); /* Nothing to do */
|
|
||||||
|
|
||||||
/* It must be a directory and also be searchable */
|
|
||||||
if (!S_ISDIR(vp->v_mode))
|
|
||||||
r = ENOTDIR;
|
|
||||||
else
|
|
||||||
r = forbidden(fp, vp, X_BIT); /* Check if dir is searchable*/
|
|
||||||
if (r != OK) return(r);
|
|
||||||
|
|
||||||
/* Everything is OK. Make the change. */
|
|
||||||
put_vnode(*result); /* release the old directory */
|
|
||||||
dup_vnode(vp);
|
|
||||||
*result = vp; /* acquire the new one */
|
|
||||||
return(OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_stat *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_stat(void)
|
|
||||||
{
|
|
||||||
/* Perform the stat(name, buf) system call. */
|
|
||||||
int r;
|
|
||||||
struct vnode *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
char fullpath[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
vir_bytes vname1, statbuf;
|
|
||||||
size_t vname1_length;
|
|
||||||
|
|
||||||
vname1 = job_m_in.m_lc_vfs_stat.name;
|
|
||||||
vname1_length = job_m_in.m_lc_vfs_stat.len;
|
|
||||||
statbuf = job_m_in.m_lc_vfs_stat.buf;
|
|
||||||
|
|
||||||
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
|
|
||||||
if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
|
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
||||||
r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf);
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
|
|
||||||
put_vnode(vp);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_fstat *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_fstat(void)
|
|
||||||
{
|
|
||||||
/* Perform the fstat(fd, buf) system call. */
|
|
||||||
register struct filp *rfilp;
|
|
||||||
int r, rfd;
|
|
||||||
vir_bytes statbuf;
|
|
||||||
|
|
||||||
statbuf = job_m_in.m_lc_vfs_fstat.buf;
|
|
||||||
rfd = job_m_in.m_lc_vfs_fstat.fd;
|
|
||||||
|
|
||||||
/* Is the file descriptor valid? */
|
|
||||||
if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
|
|
||||||
|
|
||||||
r = req_stat(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr,
|
|
||||||
who_e, statbuf);
|
|
||||||
|
|
||||||
unlock_filp(rfilp);
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* update_statvfs *
|
|
||||||
*===========================================================================*/
|
|
||||||
int update_statvfs(struct vmnt *vmp, struct statvfs *buf)
|
|
||||||
{
|
|
||||||
/* Get statistics from a file system, and cache part of the results. */
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if ((r = req_statvfs(vmp->m_fs_e, buf)) != OK)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
vmp->m_stats.f_flag = buf->f_flag;
|
|
||||||
vmp->m_stats.f_bsize = buf->f_bsize;
|
|
||||||
vmp->m_stats.f_frsize = buf->f_frsize;
|
|
||||||
vmp->m_stats.f_iosize = buf->f_iosize;
|
|
||||||
|
|
||||||
vmp->m_stats.f_blocks = buf->f_blocks;
|
|
||||||
vmp->m_stats.f_bfree = buf->f_bfree;
|
|
||||||
vmp->m_stats.f_bavail = buf->f_bavail;
|
|
||||||
vmp->m_stats.f_bresvd = buf->f_bresvd;
|
|
||||||
|
|
||||||
vmp->m_stats.f_files = buf->f_files;
|
|
||||||
vmp->m_stats.f_ffree = buf->f_ffree;
|
|
||||||
vmp->m_stats.f_favail = buf->f_favail;
|
|
||||||
vmp->m_stats.f_fresvd = buf->f_fresvd;
|
|
||||||
|
|
||||||
vmp->m_stats.f_syncreads = buf->f_syncreads;
|
|
||||||
vmp->m_stats.f_syncwrites = buf->f_syncwrites;
|
|
||||||
|
|
||||||
vmp->m_stats.f_asyncreads = buf->f_asyncreads;
|
|
||||||
vmp->m_stats.f_asyncwrites = buf->f_asyncwrites;
|
|
||||||
|
|
||||||
vmp->m_stats.f_namemax = buf->f_namemax;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* fill_statvfs *
|
|
||||||
*===========================================================================*/
|
|
||||||
static int fill_statvfs(struct vmnt *vmp, endpoint_t endpt, vir_bytes buf_addr,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
/* Fill a statvfs structure in a userspace process. First let the target file
|
|
||||||
* server fill in most fields, or use the cached copy if ST_NOWAIT is given.
|
|
||||||
* Then fill in some remaining fields with local information. Finally, copy
|
|
||||||
* the result to user space.
|
|
||||||
*/
|
|
||||||
struct statvfs buf;
|
|
||||||
|
|
||||||
if (!(flags & ST_NOWAIT)) {
|
|
||||||
/* Get fresh statistics from the file system. */
|
|
||||||
if (update_statvfs(vmp, &buf) != OK)
|
|
||||||
return EIO;
|
|
||||||
} else {
|
|
||||||
/* Use the cached statistics. */
|
|
||||||
memset(&buf, 0, sizeof(buf));
|
|
||||||
|
|
||||||
buf.f_flag = vmp->m_stats.f_flag;
|
|
||||||
buf.f_bsize = vmp->m_stats.f_bsize;
|
|
||||||
buf.f_frsize = vmp->m_stats.f_frsize;
|
|
||||||
buf.f_iosize = vmp->m_stats.f_iosize;
|
|
||||||
|
|
||||||
buf.f_blocks = vmp->m_stats.f_blocks;
|
|
||||||
buf.f_bfree = vmp->m_stats.f_bfree;
|
|
||||||
buf.f_bavail = vmp->m_stats.f_bavail;
|
|
||||||
buf.f_bresvd = vmp->m_stats.f_bresvd;
|
|
||||||
|
|
||||||
buf.f_files = vmp->m_stats.f_files;
|
|
||||||
buf.f_ffree = vmp->m_stats.f_ffree;
|
|
||||||
buf.f_favail = vmp->m_stats.f_favail;
|
|
||||||
buf.f_fresvd = vmp->m_stats.f_fresvd;
|
|
||||||
|
|
||||||
buf.f_syncreads = vmp->m_stats.f_syncreads;
|
|
||||||
buf.f_syncwrites = vmp->m_stats.f_syncwrites;
|
|
||||||
|
|
||||||
buf.f_asyncreads = vmp->m_stats.f_asyncreads;
|
|
||||||
buf.f_asyncwrites = vmp->m_stats.f_asyncwrites;
|
|
||||||
|
|
||||||
buf.f_namemax = vmp->m_stats.f_namemax;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vmp->m_flags & VMNT_READONLY)
|
|
||||||
buf.f_flag |= ST_RDONLY;
|
|
||||||
|
|
||||||
buf.f_fsid = (unsigned long)vmp->m_dev;
|
|
||||||
buf.f_fsidx.__fsid_val[0] = (long)vmp->m_dev; /* This is what is done on NetBSD */
|
|
||||||
buf.f_fsidx.__fsid_val[1] = 0; /* Here they convert the FS type name into a number. */
|
|
||||||
|
|
||||||
strlcpy(buf.f_fstypename, vmp->m_fstype, sizeof(buf.f_fstypename));
|
|
||||||
strlcpy(buf.f_mntonname, vmp->m_mount_path, sizeof(buf.f_mntonname));
|
|
||||||
strlcpy(buf.f_mntfromname, vmp->m_mount_dev, sizeof(buf.f_mntfromname));
|
|
||||||
|
|
||||||
return sys_datacopy_wrapper(SELF, (vir_bytes) &buf,
|
|
||||||
endpt, buf_addr, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_statvfs *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_statvfs(void)
|
|
||||||
{
|
|
||||||
/* Perform the statvfs1(name, buf, flags) system call. */
|
|
||||||
int r, flags;
|
|
||||||
struct vnode *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
char fullpath[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
vir_bytes vname1, statbuf;
|
|
||||||
size_t vname1_length;
|
|
||||||
|
|
||||||
vname1 = job_m_in.m_lc_vfs_statvfs1.name;
|
|
||||||
vname1_length = job_m_in.m_lc_vfs_statvfs1.len;
|
|
||||||
statbuf = job_m_in.m_lc_vfs_statvfs1.buf;
|
|
||||||
flags = job_m_in.m_lc_vfs_statvfs1.flags;
|
|
||||||
|
|
||||||
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
|
|
||||||
if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
|
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
||||||
r = fill_statvfs(vp->v_vmnt, who_e, statbuf, flags);
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
|
|
||||||
put_vnode(vp);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_fstatvfs *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_fstatvfs(void)
|
|
||||||
{
|
|
||||||
/* Perform the fstatvfs1(fd, buf, flags) system call. */
|
|
||||||
register struct filp *rfilp;
|
|
||||||
int r, rfd, flags;
|
|
||||||
vir_bytes statbuf;
|
|
||||||
|
|
||||||
rfd = job_m_in.m_lc_vfs_statvfs1.fd;
|
|
||||||
statbuf = job_m_in.m_lc_vfs_statvfs1.buf;
|
|
||||||
flags = job_m_in.m_lc_vfs_statvfs1.flags;
|
|
||||||
|
|
||||||
/* Is the file descriptor valid? */
|
|
||||||
if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
|
|
||||||
r = fill_statvfs(rfilp->filp_vno->v_vmnt, who_e, statbuf, flags);
|
|
||||||
|
|
||||||
unlock_filp(rfilp);
|
|
||||||
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_getvfsstat *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_getvfsstat(void)
|
|
||||||
{
|
|
||||||
/* Perform the getvfsstat(buf, bufsize, flags) system call. */
|
|
||||||
struct vmnt *vmp;
|
|
||||||
vir_bytes buf;
|
|
||||||
size_t bufsize;
|
|
||||||
int r, flags, count, do_lock;
|
|
||||||
|
|
||||||
buf = job_m_in.m_lc_vfs_getvfsstat.buf;
|
|
||||||
bufsize = job_m_in.m_lc_vfs_getvfsstat.len;
|
|
||||||
flags = job_m_in.m_lc_vfs_getvfsstat.flags;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
if (buf != 0) {
|
|
||||||
/* We only need to lock target file systems if we are going to query
|
|
||||||
* them. This will only happen if ST_NOWAIT is not given. If we do
|
|
||||||
* not lock, we rely on the VMNT_CANSTAT flag to protect us from
|
|
||||||
* concurrent (un)mount operations. Note that procfs relies on
|
|
||||||
* ST_NOWAIT calls being lock free, as it is a file system itself.
|
|
||||||
*/
|
|
||||||
do_lock = !(flags & ST_NOWAIT);
|
|
||||||
|
|
||||||
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
|
|
||||||
/* If there is no more space, return the count so far. */
|
|
||||||
if (bufsize < sizeof(struct statvfs))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Lock the file system before checking any fields. */
|
|
||||||
if (do_lock && (r = lock_vmnt(vmp, VMNT_READ)) != OK)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* Obtain information for this file system, if it is in use and
|
|
||||||
* can be reported. File systems that are being (un)mounted
|
|
||||||
* are skipped, as is PFS. The fill call will block only if
|
|
||||||
* ST_NOWAIT was not given.
|
|
||||||
*/
|
|
||||||
if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT)) {
|
|
||||||
if ((r = fill_statvfs(vmp, who_e, buf, flags)) != OK) {
|
|
||||||
if (do_lock)
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
buf += sizeof(struct statvfs);
|
|
||||||
bufsize -= sizeof(struct statvfs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (do_lock)
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Just report a file system count. No need to lock, as above. */
|
|
||||||
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
|
|
||||||
if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT))
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* do_lstat *
|
|
||||||
*===========================================================================*/
|
|
||||||
int do_lstat(void)
|
|
||||||
{
|
|
||||||
/* Perform the lstat(name, buf) system call. */
|
|
||||||
struct vnode *vp;
|
|
||||||
struct vmnt *vmp;
|
|
||||||
int r;
|
|
||||||
char fullpath[PATH_MAX];
|
|
||||||
struct lookup resolve;
|
|
||||||
vir_bytes vname1, statbuf;
|
|
||||||
size_t vname1_length;
|
|
||||||
|
|
||||||
vname1 = job_m_in.m_lc_vfs_stat.name;
|
|
||||||
vname1_length = job_m_in.m_lc_vfs_stat.len;
|
|
||||||
statbuf = job_m_in.m_lc_vfs_stat.buf;
|
|
||||||
|
|
||||||
lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
|
|
||||||
resolve.l_vmnt_lock = VMNT_READ;
|
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
|
||||||
|
|
||||||
if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
|
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
||||||
r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf);
|
|
||||||
|
|
||||||
unlock_vnode(vp);
|
|
||||||
unlock_vmnt(vmp);
|
|
||||||
|
|
||||||
put_vnode(vp);
|
|
||||||
return(r);
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# Makefile for VM server
|
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= vm
|
|
||||||
SRCS= main.c alloc.c utility.c exit.c fork.c break.c \
|
|
||||||
mmap.c slaballoc.c region.c pagefaults.c pagetable.c \
|
|
||||||
rs.c pb.c regionavl.c \
|
|
||||||
mem_anon.c mem_directphys.c mem_anon_contig.c mem_shared.c \
|
|
||||||
mem_cache.c cache.c vfs.c mem_file.c fdref.c acl.c
|
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm"
|
|
||||||
LDFLAGS+= -T ${.CURDIR}/arch/${MACHINE_ARCH}/vm.lds
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.if ${MKPAE:Uno} != "no"
|
|
||||||
CPPFLAGS+= -DPAE=1
|
|
||||||
.endif
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS} ${LIBEXEC}
|
|
||||||
LDADD+= -lsys -lexec
|
|
||||||
|
|
||||||
CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/${MACHINE_ARCH}
|
|
||||||
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
# For all other services, magic instrumentation involves instrumenting the
|
|
||||||
# libc malloc code, hooking its nested mmap/munmap calls, and ignoring its
|
|
||||||
# data. For VM, we need to do the exact opposite, since for VM, the malloc
|
|
||||||
# state is transferred as is. Thus, if the magic pass is enabled, tell it
|
|
||||||
# to skip the regular malloc instrumentation features.
|
|
||||||
MAGICFLAGS= -magic-disable-mem-functions -magic-disable-malloc-skip
|
|
||||||
|
|
||||||
.include "arch/${MACHINE_ARCH}/Makefile.inc"
|
|
||||||
.include <minix.service.mk>
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
.include <bsd.own.mk>
|
|
||||||
|
|
||||||
PROG= trace
|
|
||||||
SRCS= call.o error.o escape.o format.o ioctl.o kernel.o mem.o output.o \
|
|
||||||
proc.o signal.o trace.o
|
|
||||||
.PATH: ${.CURDIR}/service
|
|
||||||
SRCS+= pm.o vfs.o rs.o mib.o vm.o ipc.o
|
|
||||||
.PATH: ${.CURDIR}/ioctl
|
|
||||||
SRCS+= block.o char.o net.o svrctl.o
|
|
||||||
|
|
||||||
CPPFLAGS+= -D_MINIX_SYSTEM=1 -I${.CURDIR} -I${NETBSDSRCDIR}/minix
|
|
||||||
|
|
||||||
COPTS.format.c+= -Wno-format-nonliteral
|
|
||||||
|
|
||||||
error.c: error.awk ${NETBSDSRCDIR}/sys/sys/errno.h
|
|
||||||
${TOOL_AWK} -f ${.ALLSRC} > ${.TARGET}
|
|
||||||
|
|
||||||
signal.c: signal.awk ${NETBSDSRCDIR}/sys/sys/signal.h
|
|
||||||
${TOOL_AWK} -f ${.ALLSRC} > ${.TARGET}
|
|
||||||
|
|
||||||
CLEANFILES+= error.c signal.c
|
|
||||||
|
|
||||||
WARNS?= 5
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
||||||
Loading…
Reference in New Issue
Block a user