mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
89
.gitignore
vendored
Normal file
89
.gitignore
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#
|
||||||
|
# Ignore compiler generated files
|
||||||
|
#
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.d
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ignore generated executables and other generated files
|
||||||
|
#
|
||||||
|
cmsfs-fuse/cmsfs-fuse
|
||||||
|
cpacfstats/cpacfstats
|
||||||
|
cpacfstats/cpacfstatsd
|
||||||
|
cpumf/bin/chcpumf
|
||||||
|
cpumf/bin/cpumf_helper
|
||||||
|
cpumf/bin/lscpumf
|
||||||
|
cpuplugd/cpuplugd
|
||||||
|
dasdfmt/dasdfmt
|
||||||
|
dasdinfo/dasdinfo
|
||||||
|
dasdview/dasdview
|
||||||
|
dump2tar/src/dump2tar
|
||||||
|
fdasd/fdasd
|
||||||
|
hmcdrvfs/hmcdrvfs
|
||||||
|
hyptop/hyptop
|
||||||
|
ip_watcher/xcec-bridge
|
||||||
|
ipl_tools/chreipl
|
||||||
|
ipl_tools/chshut
|
||||||
|
ipl_tools/lsreipl
|
||||||
|
ipl_tools/lsshut
|
||||||
|
iucvterm/bin/chiucvallow
|
||||||
|
iucvterm/bin/ts-shell
|
||||||
|
iucvterm/etc/ts-shell.conf
|
||||||
|
iucvterm/po/iucvterm.pot
|
||||||
|
iucvterm/src/iucvconn
|
||||||
|
iucvterm/src/iucvtty
|
||||||
|
iucvterm/src/ttyrun
|
||||||
|
iucvterm/test/test_afiucv
|
||||||
|
libutil/util_base_example
|
||||||
|
libutil/util_file_example
|
||||||
|
libutil/util_libc_example
|
||||||
|
libutil/util_opt_command_example
|
||||||
|
libutil/util_opt_example
|
||||||
|
libutil/util_panic_example
|
||||||
|
libutil/util_path_example
|
||||||
|
libutil/util_prg_example
|
||||||
|
libutil/util_rec_example
|
||||||
|
libutil/util_scandir_example
|
||||||
|
libzds/libzds.a
|
||||||
|
mon_tools/mon_fsstatd
|
||||||
|
mon_tools/mon_procd
|
||||||
|
osasnmpd/osasnmpd
|
||||||
|
qetharp/qetharp
|
||||||
|
qethqoat/qethqoat
|
||||||
|
systemd/cpacfstatsd.service
|
||||||
|
systemd/iucvtty-login@.service
|
||||||
|
systemd/ttyrun-getty@.service
|
||||||
|
tape390/tape390_crypt
|
||||||
|
tape390/tape390_display
|
||||||
|
tunedasd/src/tunedasd
|
||||||
|
vmconvert/vmconvert
|
||||||
|
vmcp/vmcp
|
||||||
|
vmur/vmur
|
||||||
|
zconf/chp/chchp
|
||||||
|
zconf/chp/lschp
|
||||||
|
zconf/css/lscss
|
||||||
|
zconf/qeth/lsqeth
|
||||||
|
zconf/scm/lsscm
|
||||||
|
zconf/zcrypt/chzcrypt
|
||||||
|
zconf/zcrypt/lszcrypt
|
||||||
|
zdev/src/chzdev
|
||||||
|
zdev/src/chzdev_usage.c
|
||||||
|
zdev/src/lszdev
|
||||||
|
zdev/src/lszdev_usage.c
|
||||||
|
zdsfs/zdsfs
|
||||||
|
zdump/zgetdump
|
||||||
|
zfcpdump/cpioinit
|
||||||
|
zfcpdump/zfcpdump_part
|
||||||
|
zfcpdump/zfcpdump_part.rd
|
||||||
|
ziomon/ziomon_mgr
|
||||||
|
ziomon/ziomon_util
|
||||||
|
ziomon/ziomon_zfcpdd
|
||||||
|
ziomon/ziorep_traffic
|
||||||
|
ziomon/ziorep_utilization
|
||||||
|
zipl/boot/*.bin
|
||||||
|
zipl/boot/*.exec
|
||||||
|
zipl/boot/data.h
|
||||||
|
zipl/src/chreipl_helper.device-mapper
|
||||||
|
zipl/src/zipl
|
||||||
|
zkey/zkey
|
||||||
93
AUTHORS.md
Normal file
93
AUTHORS.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
List of all individuals having contributed content to s390-tools
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
- Alexey Ishchuk
|
||||||
|
- Andreas Herrmann
|
||||||
|
- Andre Wild
|
||||||
|
- Antoinette Kaschner
|
||||||
|
- Arnd Bergmann
|
||||||
|
- Axel Wirbser
|
||||||
|
- Benjamin Block
|
||||||
|
- Carsten Otte
|
||||||
|
- Christian Borntraeger
|
||||||
|
- Christof Schmitt
|
||||||
|
- Clemens von Mann
|
||||||
|
- Despina Papadopoulou
|
||||||
|
- Eberhard Pasch
|
||||||
|
- Einar Lueck
|
||||||
|
- Erwin Vicari
|
||||||
|
- Eugene Dvurechenski
|
||||||
|
- Eugene Crosser
|
||||||
|
- Farhan Ali
|
||||||
|
- Felix Beck
|
||||||
|
- Frank Blaschka
|
||||||
|
- Frank Munzert
|
||||||
|
- Frank Pavlic
|
||||||
|
- Fritz Elfert
|
||||||
|
- Gerald Schaefer
|
||||||
|
- Gerhard Tonn
|
||||||
|
- Hans Wippel
|
||||||
|
- Hans-Joachim Picht
|
||||||
|
- Harald Freudenberger
|
||||||
|
- Heiko Carstens
|
||||||
|
- Hendrik Brueckner
|
||||||
|
- Holger Dengler
|
||||||
|
- Holger Smolinski
|
||||||
|
- Hongjie Yang
|
||||||
|
- Horst Hummel
|
||||||
|
- Ingo Franzki
|
||||||
|
- Ingo Tuchscherer
|
||||||
|
- Jan Glauber
|
||||||
|
- Jan Hoeppner
|
||||||
|
- Jan Willeke
|
||||||
|
- Jean-Baptiste Joret
|
||||||
|
- Jens Remus
|
||||||
|
- Jochen Roehrig
|
||||||
|
- Julian Wiedmann
|
||||||
|
- Karsten Graul
|
||||||
|
- Kittipon Meesompop
|
||||||
|
- Klaus-Dieter Wacker
|
||||||
|
- Lakhvich Dmitriy
|
||||||
|
- Mark Dettinger
|
||||||
|
- Martin Kammerer
|
||||||
|
- Martin Peschke
|
||||||
|
- Martin Petermann
|
||||||
|
- Martin Schwidefsky
|
||||||
|
- Maxim Shchetynin
|
||||||
|
- Melissa Howland
|
||||||
|
- Michael Ernst
|
||||||
|
- Michael Holzheu
|
||||||
|
- Michael Mueller
|
||||||
|
- Mijo Safradin
|
||||||
|
- Mikhail Zaslonko
|
||||||
|
- Peter Oberparleiter
|
||||||
|
- Peter Tiedemann
|
||||||
|
- Raimund Schroeder
|
||||||
|
- Ralph Wuerthner
|
||||||
|
- Rene Trumpp
|
||||||
|
- Rolf Schaefer
|
||||||
|
- Sa Liu
|
||||||
|
- Sascha Silbe
|
||||||
|
- Sebastian Ott
|
||||||
|
- Seshagiri N. Ippili
|
||||||
|
- Simon Sturm
|
||||||
|
- Stefan Bader
|
||||||
|
- Stefan Haberland
|
||||||
|
- Stefan Raspl
|
||||||
|
- Stefan Reimbold
|
||||||
|
- Stefan Weinhuber
|
||||||
|
- Steffen Maier
|
||||||
|
- Steffen Thoss
|
||||||
|
- Susanne Wintenberger
|
||||||
|
- Sven Schuetz
|
||||||
|
- Swen Schillig
|
||||||
|
- Taraka R. Bodireddy
|
||||||
|
- Thomas Heidrich
|
||||||
|
- Thomas Richter
|
||||||
|
- Thomas Spatzier
|
||||||
|
- Thomas Weber
|
||||||
|
- Ursula Braun
|
||||||
|
- Utz Bacher
|
||||||
|
- Viktor Mihajlovski
|
||||||
|
- Volker Sameske
|
||||||
|
- Wolfgang Taphorn
|
||||||
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Release history for s390-tools (MIT version)
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
* __v2.0.0 (2017-08-xx)__
|
||||||
|
|
||||||
|
- Publish package under the MIT license with the same contents as
|
||||||
|
the already available s390-tools-1.39.0
|
||||||
|
|
||||||
|
Previous releases of s390-tools can be found on the IBM Developer Works web pages:
|
||||||
|
|
||||||
|
- https://www.ibm.com/developerworks/linux/linux390/s390-tools.html
|
||||||
65
CONTRIBUTING.md
Normal file
65
CONTRIBUTING.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
Contributing to s390-tools
|
||||||
|
==========================
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
All contributions have to be submitted under the MIT license. See also
|
||||||
|
the [LICENSE](LICENSE) file.
|
||||||
|
|
||||||
|
Developer's Certificate of Origin and Signed-off-by
|
||||||
|
---------------------------------------------------
|
||||||
|
The sign-off is a simple line at the end of the explanation for the patch,
|
||||||
|
which certifies that you wrote it or otherwise have the right to pass it on as
|
||||||
|
an open-source patch.
|
||||||
|
|
||||||
|
With the Signed-off-by line you certify the below:
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
If you can certify the above, just add a line saying:
|
||||||
|
|
||||||
|
```
|
||||||
|
Signed-off-by: Random Developer <random@developer.example.org>
|
||||||
|
```
|
||||||
|
|
||||||
|
Please use your real name (no pseudonyms or anonymous contributions).
|
||||||
|
|
||||||
|
Submitting code
|
||||||
|
---------------
|
||||||
|
The preferred way is to create a github pull request for your code.
|
||||||
|
|
||||||
|
Coding guidelines
|
||||||
|
-----------------
|
||||||
|
For s390-tools the preferred language is C. We provide libraries, e.g. libutil
|
||||||
|
that should be used by all tools if possible.
|
||||||
|
|
||||||
|
The coding style is based on the Linux kernel guidelines. Therefore, use
|
||||||
|
the checkpatch tool [1] for verification before you submit a patch.
|
||||||
|
|
||||||
|
[1] https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright IBM Corp. 2001, 2017
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
48
Makefile
Normal file
48
Makefile
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
|
||||||
|
|
||||||
|
# Include common definitions
|
||||||
|
include common.mak
|
||||||
|
|
||||||
|
LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw
|
||||||
|
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||||
|
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||||
|
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||||
|
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||||
|
systemd hmcdrvfs cpacfstats zdev dump2tar zkey
|
||||||
|
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
||||||
|
|
||||||
|
all: $(TOOL_DIRS)
|
||||||
|
clean: $(TOOL_DIRS)
|
||||||
|
install: $(TOOL_DIRS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# For simple "make" we explicitly set the MAKECMDGOALS to "all".
|
||||||
|
#
|
||||||
|
ifeq ($(MAKECMDGOALS),)
|
||||||
|
MAKECMDGOALS = all
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# We have to build the libraries before the tools are built. Otherwise
|
||||||
|
# the tools would trigger parallel "make -C" builds for libraries in
|
||||||
|
# case of "make -j".
|
||||||
|
#
|
||||||
|
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
|
||||||
|
# "foreach" to generate a ";" separated list of "make -C <target>".
|
||||||
|
# For example the the expansion for "make clean all" is:
|
||||||
|
#
|
||||||
|
# $(MAKE) -C $@ [..] clean ; $(MAKE) -C $@ [...] all ;
|
||||||
|
#
|
||||||
|
# This ensures that the commandline targets are serialized and also "make -j"
|
||||||
|
# works as expected, e.g. "make clean all -j 20".
|
||||||
|
#
|
||||||
|
|
||||||
|
$(TOOL_DIRS): $(LIB_DIRS)
|
||||||
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
|
.PHONY: $(TOOL_DIRS)
|
||||||
|
|
||||||
|
$(LIB_DIRS):
|
||||||
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
|
.PHONY: $(LIB_DIRS)
|
||||||
361
README.md
Normal file
361
README.md
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
|
||||||
|
s390-tools
|
||||||
|
==========
|
||||||
|
|
||||||
|
The s390-tools package contains the source tree of a set of user space
|
||||||
|
utilities for use with the s390 Linux kernel and device drivers.
|
||||||
|
|
||||||
|
The package also contains the following files:
|
||||||
|
|
||||||
|
* [CONTRIBUTING](CONTRIBUTING.md): Contribution guidelines
|
||||||
|
* [LICENSE](LICENSE): The MIT license that applies to this package
|
||||||
|
* [CHANGELOG](CHANGELOG.md): The history of s390-tools versions
|
||||||
|
* [AUTHORS](AUTHORS.md): A list of all authors of the s390-tools package
|
||||||
|
|
||||||
|
Package contents
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* dasdfmt:
|
||||||
|
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||||
|
z/OS compatible disk layout.
|
||||||
|
|
||||||
|
* fdasd:
|
||||||
|
Create or modify partitions on ECKD DASDs formatted with the z/OS
|
||||||
|
compatible disk layout.
|
||||||
|
|
||||||
|
* dasdview:
|
||||||
|
Display DASD and VTOC information or dump the contents of a DASD to the
|
||||||
|
console.
|
||||||
|
|
||||||
|
* dasdinfo:
|
||||||
|
Display unique DASD ID, either UID or volser.
|
||||||
|
|
||||||
|
* udev rules:
|
||||||
|
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||||
|
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||||
|
- 60-readahead.rules: udev rules to set increased "default max readahead".
|
||||||
|
- 40-z90crypt.rules: udev rules for z90crypt driver
|
||||||
|
|
||||||
|
* systemd units:
|
||||||
|
- cpi.service: Unit to apply CPI settings
|
||||||
|
- dumpconf.service: Unit to configure dump on panic for s390
|
||||||
|
- mon_fsstatd.service: Unit for mon_fsstatd
|
||||||
|
- mon_procd.service: Unit for mon_procd
|
||||||
|
- iucvtty-login@.service: Instance unit to manage iucvtty instances
|
||||||
|
- ttyrun-getty@.service: Instance unit to manage ttyrun
|
||||||
|
|
||||||
|
* zipl:
|
||||||
|
Make DASDs or tapes bootable for system IPL or system dump.
|
||||||
|
|
||||||
|
* zgetdump:
|
||||||
|
Retrieve system dumps from either tapes or DASDs.
|
||||||
|
|
||||||
|
* qetharp:
|
||||||
|
Read and flush the ARP cache on OSA Express network cards.
|
||||||
|
|
||||||
|
* tape390_display:
|
||||||
|
Display information on the message display facility of a s390 tape
|
||||||
|
device.
|
||||||
|
|
||||||
|
* tape390_crypt:
|
||||||
|
Control and query crypto settings for 3592 tape devices.
|
||||||
|
|
||||||
|
* osasnmpd:
|
||||||
|
NET-SNMP subagent implementing MIBs provided by OSA-Express
|
||||||
|
features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet.
|
||||||
|
|
||||||
|
* qethconf:
|
||||||
|
bash shell script simplifying the usage of qeth IPA (IP address
|
||||||
|
takeover), VIPA (Virtual IP address) and Proxy ARP.
|
||||||
|
|
||||||
|
* dbginfo.sh:
|
||||||
|
Shell script collecting useful information about the current system for
|
||||||
|
debugging purposes.
|
||||||
|
|
||||||
|
* zfcpdump:
|
||||||
|
Dump tool to create system dumps on fibre channel attached SCSI disk
|
||||||
|
partitions. It is installed using the "zipl -d" command.
|
||||||
|
|
||||||
|
* ip_watcher:
|
||||||
|
Provides HiperSockets Network Concentrator functionality.
|
||||||
|
It looks for addresses in the HiperSockets and sets them as Proxy ARP
|
||||||
|
on the OSA cards. It also adds routing entries for all IP addresses
|
||||||
|
configured on active HiperSockets devices.
|
||||||
|
Use start_hsnc.sh to start HiperSockets Network Concentrator.
|
||||||
|
|
||||||
|
* tunedasd:
|
||||||
|
Adjust tunable parameters on DASD devices.
|
||||||
|
|
||||||
|
* vmconvert:
|
||||||
|
Convert system dumps created by the z/VM VMDUMP command into dumps with
|
||||||
|
LKCD format. These LKCD dumps can then be analyzed with the dump analysis
|
||||||
|
tool lcrash.
|
||||||
|
|
||||||
|
* vmcp:
|
||||||
|
Send commands from Linux as a z/VM guest to the z/VM control program (CP).
|
||||||
|
Call vmcp with the CP command as an argument. The response of z/VM is
|
||||||
|
written to the standard output.
|
||||||
|
|
||||||
|
* vmur:
|
||||||
|
Work with z/VM spool file queues (reader, punch, printer).
|
||||||
|
|
||||||
|
* zfcpdbf:
|
||||||
|
Display debug data of zfcp. zfcp provides traces via the s390 debug
|
||||||
|
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
||||||
|
several traces, make it more readable etc.
|
||||||
|
|
||||||
|
* scsi_logging_level:
|
||||||
|
Create, get or set the logging level for the SCSI logging facility.
|
||||||
|
|
||||||
|
* zconf:
|
||||||
|
Set of scripts to configure and list status information of Linux on s390
|
||||||
|
devices.
|
||||||
|
- chccwdev: Modify generic attributes of channel attached devices.
|
||||||
|
- lscss: List channel subsystem devices.
|
||||||
|
- lsdasd: List channel attached direct access storage devices (DASD).
|
||||||
|
- lsqeth: List all qeth-based network devices with their corresponding
|
||||||
|
settings.
|
||||||
|
- lstape: List tape devices, both channel and FCP attached.
|
||||||
|
- lszfcp: Show sysfs information about zfcp adapters, ports and units
|
||||||
|
that are online.
|
||||||
|
- lschp: List information about available channel-paths.
|
||||||
|
- lsscm: List information about available Storage Class Memory
|
||||||
|
Increments.
|
||||||
|
- chchp: Modify channel-path state.
|
||||||
|
- lsluns: List available SCSI LUNs depending on adapter or port.
|
||||||
|
- lszcrypt: Show Information about zcrypt devices and configuration.
|
||||||
|
- chzcrypt: Modify the zcrypt configuration.
|
||||||
|
- znetconf: List and configure network devices for s390 network adapters.
|
||||||
|
- cio_ignore: Query and modify the contents of the CIO device driver
|
||||||
|
blacklist.
|
||||||
|
- lsmem: Display the online status of the available memory.
|
||||||
|
- chmem: Set hotplug memory online or offline.
|
||||||
|
- dasdstat: Configure and format the debugfs based DASD statistics data.
|
||||||
|
|
||||||
|
* zkey:
|
||||||
|
Use the zkey tool to generate secure AES keys that are enciphered
|
||||||
|
with a master key of an IBM cryptographic adapter in CCA coprocessor mode.
|
||||||
|
You can also use the zkey tool to validate and re-encipher secure
|
||||||
|
AES keys.
|
||||||
|
|
||||||
|
* dumpconf:
|
||||||
|
Configure the dump device used for system dump in case a kernel
|
||||||
|
panic occurs. This tool can also be used by the "dumpconf" systemd unit
|
||||||
|
or as System V init script in /etc/init.d.
|
||||||
|
Prerequisite for dumpconf is a Linux kernel with the "dump on panic"
|
||||||
|
feature.
|
||||||
|
|
||||||
|
* mon_statd:
|
||||||
|
Linux - z/VM monitoring daemons.
|
||||||
|
|
||||||
|
- mon_fsstatd: Daemon that writes file system utilization data to the
|
||||||
|
z/VM monitor stream.
|
||||||
|
|
||||||
|
- mon_procd: Daemon that writes process information data to the z/VM
|
||||||
|
monitor stream.
|
||||||
|
|
||||||
|
* cpuplugd:
|
||||||
|
Manages CPU and memory resources based on a set of rules. Depending on
|
||||||
|
the workload, CPUs can be enabled or disabled. The amount of memory can
|
||||||
|
be increased or decreased exploiting the CMM1 feature.
|
||||||
|
|
||||||
|
* ipl_tools:
|
||||||
|
Tool set to configure and list re-IPL and shutdown actions.
|
||||||
|
- lsreipl: List information of re-IPL device.
|
||||||
|
- chreipl: Change re-IPL device settings.
|
||||||
|
- lsshut: List the actions that are configured as responses to of halt,
|
||||||
|
poff, reboot or panic.
|
||||||
|
- chshut: Change the actions that are to result from of halt, poff,
|
||||||
|
reboot or panic.
|
||||||
|
|
||||||
|
* ziomon tools:
|
||||||
|
Tool set to collect data for zfcp performance analysis and report.
|
||||||
|
|
||||||
|
* iucvterm:
|
||||||
|
z/VM IUCV terminal applications.
|
||||||
|
|
||||||
|
A set of applications to provide terminal access via the z/VM Inter-User
|
||||||
|
Communication Vehicle (IUCV). The terminal access does not require an
|
||||||
|
active TCP/IP connection between two Linux guest operating systems.
|
||||||
|
|
||||||
|
- iucvconn: Application to establish a terminal connection via z/VM IUCV.
|
||||||
|
- iucvtty: Application to provide terminal access via z/VM IUCV.
|
||||||
|
- ts-shell: Terminal server shell to authorize and control IUCV terminal
|
||||||
|
connections for individual Linux users.
|
||||||
|
* ttyrun:
|
||||||
|
Depending on your setup, Linux on s390 might or might not provide a
|
||||||
|
particular terminal or console. The ttyrun tool safely starts getty
|
||||||
|
programs and prevents respawns through the init program, if a terminal
|
||||||
|
is not available.
|
||||||
|
|
||||||
|
* cmsfs-fuse:
|
||||||
|
Read and write files stored on a z/VM CMS disk. The cmsfs-fuse file system
|
||||||
|
translates the record-based EDF file system on the CMS disk to UNIX
|
||||||
|
semantics. It is possible to mount a CMS disk and use common Linux tools
|
||||||
|
to access the files on the disk.
|
||||||
|
|
||||||
|
* hmcdrvfs:
|
||||||
|
Provide (read-only) access to files stored on a (assigned) DVD inserted
|
||||||
|
in a HMC drive. The command creates a FUSE.HMCDRVFS filesystem at the
|
||||||
|
specified mount point. The feature works with either the LPAR or the
|
||||||
|
z/VM hypervisor. But note especially for z/VM that the DVD must be
|
||||||
|
assigned to the associated system image (LPAR).
|
||||||
|
|
||||||
|
* hyptop:
|
||||||
|
Provide a dynamic real-time view of a s390 hypervisor environment.
|
||||||
|
The tool works with the z/VM and LPAR hypervisor. Depending on the available
|
||||||
|
data it shows e.g. CPU and memory consumption of active LPARs or z/VM
|
||||||
|
virtual guests. The tool provides a curses based user interface similar
|
||||||
|
to the popular Linux 'top' command.
|
||||||
|
|
||||||
|
* qethqoat:
|
||||||
|
Query the OSA address table and display physical and logical device
|
||||||
|
information.
|
||||||
|
|
||||||
|
* zdsfs:
|
||||||
|
Mount a z/OS DASD as Linux file system.
|
||||||
|
|
||||||
|
* CPU-measurement facilities (CPU-MF) tools:
|
||||||
|
Use the lscpumf tool to display information about the CPU-measurement
|
||||||
|
counter and sampling facilities. Use the chcpumf tool to control the
|
||||||
|
sampling facility support.
|
||||||
|
|
||||||
|
* cpacfstats:
|
||||||
|
The cpacfstats tools provide a client/server application set to monitor
|
||||||
|
and maintain CPACF activity counters.
|
||||||
|
|
||||||
|
* zdev:
|
||||||
|
Provides two tools to modify (chzdev) and display (lszdev) the persistent
|
||||||
|
configuration of devices and device drivers that are specific to the s390
|
||||||
|
platform.
|
||||||
|
|
||||||
|
* dump2tar:
|
||||||
|
dump2tar is a tool for creating a tar archive from the contents of
|
||||||
|
arbitrary files. It works even when the size of the actual file content
|
||||||
|
is not known beforehand (e.g. FIFOs, sysfs files).
|
||||||
|
|
||||||
|
For more information refer to the following publications:
|
||||||
|
|
||||||
|
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||||
|
* "Using the dump tools"
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
The s390-tools package has several build and runtime requirements. If your
|
||||||
|
build system does not have the required support, you can disable parts of
|
||||||
|
the s390-tools build with "`make HAVE_<LIBRARY>=0`", for example "`make
|
||||||
|
HAVE_FUSE=0`".
|
||||||
|
|
||||||
|
The following table provides an overview of the used libraries and
|
||||||
|
build options:
|
||||||
|
|
||||||
|
| __LIBRARY__|__BUILD OPTION__| __TOOLS__ |
|
||||||
|
|------------|:--------------:|:-------------------------------------:|
|
||||||
|
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump |
|
||||||
|
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||||
|
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||||
|
| pfm | `HAVE_PFM` | cpacfstats |
|
||||||
|
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||||
|
|
||||||
|
This table lists additional build or install options:
|
||||||
|
|
||||||
|
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||||
|
|----------------|:----------------:|:-------------------------------:|
|
||||||
|
| dracut | `HAVE_DRACUT` | zdev |
|
||||||
|
|
||||||
|
The s390-tools build process uses "pkg-config" if available and hard-coded
|
||||||
|
compiler and linker options otherwise.
|
||||||
|
|
||||||
|
Build and runtime requirements for specific tools
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
In the following more details on the build an runtime requirements of
|
||||||
|
the different tools are provided:
|
||||||
|
|
||||||
|
* osasnmpd:
|
||||||
|
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||||
|
installed, before building the osasnmpd subagent.
|
||||||
|
For more information on NET-SNMP refer to:
|
||||||
|
http://net-snmp.sourceforge.net
|
||||||
|
|
||||||
|
* lsluns:
|
||||||
|
For executing the lsluns script the sg_luns command must be available.
|
||||||
|
The sg_luns executable is part of the SCSI generic device driver package
|
||||||
|
(sg3 utils/sg utils).
|
||||||
|
|
||||||
|
* ziomon tools:
|
||||||
|
For running the ziomon tools the following tools/packages are required:
|
||||||
|
- Packages: blktrace, multipath-tools, sg3-utils
|
||||||
|
- Tools: rsync, tar, lsscsi
|
||||||
|
|
||||||
|
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
|
||||||
|
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
|
||||||
|
FUSE is provided by installing the fuse and libfuse packages and by a
|
||||||
|
kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package
|
||||||
|
the fuse-devel package is required.
|
||||||
|
The cmsfs-fuse tool requires FUSE version 2.8.1 or newer for full
|
||||||
|
functionality.
|
||||||
|
For further information about FUSE see: http://fuse.sourceforge.net
|
||||||
|
|
||||||
|
* hyptop:
|
||||||
|
The ncurses-devel package is required to build hyptop.
|
||||||
|
The libncurses package is required to run hyptop.
|
||||||
|
IMPORTANT: When running hyptop on a System z10 LPAR, the required minimum
|
||||||
|
microcode code level is the following:
|
||||||
|
Driver 79 MCL N24404.008 in the SE-LPAR stream
|
||||||
|
|
||||||
|
* ip_watcher/xcec-bridge:
|
||||||
|
As of s390-tools-1.10.0, the minimum required kernel level is 2.6.26.
|
||||||
|
For running ip_watcher these programs are required:
|
||||||
|
- qetharp (s390-tools)
|
||||||
|
- qethconf (s390-tools)
|
||||||
|
- route (net-tools)
|
||||||
|
|
||||||
|
* zfcpdbf:
|
||||||
|
As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38.
|
||||||
|
|
||||||
|
* cpacfstats:
|
||||||
|
For building the cpacfstats tools you need libpfm version 4 or
|
||||||
|
newer installed (libpfm-devel.rpm). Tip: you may skip the cpacfstats
|
||||||
|
build by adding `HAVE_PFN=0` to the make invocation. To run the
|
||||||
|
cpacfstats daemon the kernel needs to have performance
|
||||||
|
events enabled (check for `CONFIG_PERF_EVENTS=y`) and you need libpfm
|
||||||
|
version 4 or newer installed. A new group 'cpacfstats' needs to be
|
||||||
|
created and all users intending to use the tool should be added to
|
||||||
|
this group.
|
||||||
|
|
||||||
|
* zdev:
|
||||||
|
Depending on the boot loader and initial RAM-disk mechanism used by a
|
||||||
|
Linux distribution, specific steps may be required to make changes to
|
||||||
|
the root device configuration persistent. zdev encapsulates this root
|
||||||
|
device logic in a helper script called 'zdev-root-update': This script
|
||||||
|
is invoked whenever the root device configuration is changed and it must
|
||||||
|
ensure that the persistent root device configuration is put into effect
|
||||||
|
during boot. zdev provides a sample implementation for zdev-root-update
|
||||||
|
which can be selected during the 'make install' step using the `HAVE_DRACUT`
|
||||||
|
variable:
|
||||||
|
|
||||||
|
- `HAVE_DRACUT=1` installs a zdev-root-update helper that works with zipl
|
||||||
|
as boot loader and dracut as initial RAM-disk provider.
|
||||||
|
|
||||||
|
Distributors with different boot or RAM-disk mechanisms should provide
|
||||||
|
a custom zdev-root-update helper script.
|
||||||
|
|
||||||
|
Some functions of zdev require that the following programs are available:
|
||||||
|
|
||||||
|
- modprobe (kmod)
|
||||||
|
- udevadm (systemd)
|
||||||
|
|
||||||
|
The following programs are not required but when available will improve
|
||||||
|
the functionality of the zdev tools:
|
||||||
|
|
||||||
|
- lsblk (util-linux)
|
||||||
|
- findmnt (util-linux)
|
||||||
|
- vmcp (s390-tools)
|
||||||
|
- ip (iproute2)
|
||||||
|
|
||||||
|
* znetconf:
|
||||||
|
For running znetconf these programs are required:
|
||||||
|
- modprobe (kmod)
|
||||||
|
- vmcp (s390-tools)
|
||||||
58
cmsfs-fuse/Makefile
Normal file
58
cmsfs-fuse/Makefile
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
ifeq (${HAVE_FUSE},0)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_FUSE=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_FUSE=0
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
check_dep:
|
||||||
|
$(call check_dep, \
|
||||||
|
"cmsfs-fuse", \
|
||||||
|
"fuse.h", \
|
||||||
|
"fuse-devel or libfuse-dev", \
|
||||||
|
"HAVE_FUSE=0")
|
||||||
|
|
||||||
|
all: check_dep cmsfs-fuse
|
||||||
|
|
||||||
|
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||||
|
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
||||||
|
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||||
|
else
|
||||||
|
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||||
|
FUSE_LDLIBS = -lfuse
|
||||||
|
endif
|
||||||
|
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
|
||||||
|
LDLIBS += $(FUSE_LDLIBS) -lm
|
||||||
|
|
||||||
|
OBJECTS = cmsfs-fuse.o dasd.o amap.o config.o
|
||||||
|
|
||||||
|
CMSFS_FUSE_DIR = $(SYSCONFDIR)/cmsfs-fuse
|
||||||
|
CONFIG_FILES = filetypes.conf
|
||||||
|
|
||||||
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
cmsfs-fuse: $(OBJECTS) $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cmsfs-fuse \
|
||||||
|
$(DESTDIR)$(USRBINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 cmsfs-fuse.1 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man1
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$(CMSFS_FUSE_DIR)
|
||||||
|
for cnf in $(CONFIG_FILES); do \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 etc/$$cnf $(DESTDIR)$(CMSFS_FUSE_DIR) ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f cmsfs-fuse *.o
|
||||||
|
|
||||||
|
.PHONY: all install clean check_dep
|
||||||
292
cmsfs-fuse/amap.c
Normal file
292
cmsfs-fuse/amap.c
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* Allocation map functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#include "cmsfs-fuse.h"
|
||||||
|
#include "edf.h"
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hint where to look for the next free block (level 0 only).
|
||||||
|
* Updated if a free block is found. If the level 0 amap bitmap
|
||||||
|
* block is exhausted we still scan all amap blocks.
|
||||||
|
*/
|
||||||
|
struct amap_alloction_hint {
|
||||||
|
/* addr of amap bitmap block to check */
|
||||||
|
off_t amap_addr;
|
||||||
|
/* disk addr of the last allocated or freed block */
|
||||||
|
off_t addr;
|
||||||
|
/* offset to start of the amap data block */
|
||||||
|
off_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct amap_alloction_hint amap_hint;
|
||||||
|
|
||||||
|
static void update_amap_hint(off_t amap_addr, off_t addr)
|
||||||
|
{
|
||||||
|
amap_hint.amap_addr = amap_addr;
|
||||||
|
amap_hint.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get L1 block number from address.
|
||||||
|
*/
|
||||||
|
static int amap_blocknumber(off_t addr)
|
||||||
|
{
|
||||||
|
return addr / BYTES_PER_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the block number for a specific level.
|
||||||
|
*/
|
||||||
|
static int amap_blocknumber_level(int level, off_t addr)
|
||||||
|
{
|
||||||
|
int entry = amap_blocknumber(addr);
|
||||||
|
|
||||||
|
while (level-- > 1)
|
||||||
|
entry /= PTRS_PER_BLOCK;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return address of to the allocation map for a block number > 0.
|
||||||
|
*/
|
||||||
|
static off_t get_amap_addr(int level, off_t addr, off_t ptr)
|
||||||
|
{
|
||||||
|
int block = amap_blocknumber_level(level, addr) % PTRS_PER_BLOCK;
|
||||||
|
|
||||||
|
if (cmsfs.amap_levels == 0)
|
||||||
|
return cmsfs.amap;
|
||||||
|
|
||||||
|
if (level--) {
|
||||||
|
ptr = get_fixed_pointer(ptr + (off_t) block * PTR_SIZE);
|
||||||
|
if (!ptr)
|
||||||
|
DIE("amap invalid ptr at addr: %llx\n",
|
||||||
|
(unsigned long long) ptr +
|
||||||
|
(off_t) block * PTR_SIZE);
|
||||||
|
return get_amap_addr(level, addr, ptr);
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark disk address as allocated in alloc map.
|
||||||
|
*/
|
||||||
|
static void amap_block_set(off_t amap, int bit)
|
||||||
|
{
|
||||||
|
u8 entry;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = _read(&entry, sizeof(entry), amap);
|
||||||
|
BUG(rc < 0);
|
||||||
|
|
||||||
|
/* already used */
|
||||||
|
BUG(entry & (1 << (7 - bit)));
|
||||||
|
|
||||||
|
entry |= (1 << (7 - bit));
|
||||||
|
rc = _write(&entry, sizeof(entry), amap);
|
||||||
|
BUG(rc < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark disk address as free in alloc map. Unaligned addr is tolerated.
|
||||||
|
*/
|
||||||
|
static void amap_block_clear(off_t addr)
|
||||||
|
{
|
||||||
|
off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap);
|
||||||
|
int rc, block = amap_blocknumber(addr);
|
||||||
|
off_t disk_addr = addr;
|
||||||
|
unsigned int byte, bit;
|
||||||
|
u8 entry;
|
||||||
|
|
||||||
|
if (block > 0)
|
||||||
|
addr -= (off_t) block * BYTES_PER_BLOCK;
|
||||||
|
|
||||||
|
addr >>= BITS_PER_DATA_BLOCK;
|
||||||
|
byte = addr / 8;
|
||||||
|
bit = addr % 8;
|
||||||
|
|
||||||
|
rc = _read(&entry, sizeof(entry), amap + byte);
|
||||||
|
BUG(rc < 0);
|
||||||
|
|
||||||
|
/* already cleared */
|
||||||
|
BUG(!(entry & (1 << (7 - bit))));
|
||||||
|
|
||||||
|
entry &= ~(1 << (7 - bit));
|
||||||
|
rc = _write(&entry, sizeof(entry), amap + byte);
|
||||||
|
BUG(rc < 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the freed addr is lower set the hint to it to ensure
|
||||||
|
* the amap bitmap is packed from the start. That way we do not
|
||||||
|
* need an extra check if the bitmap entry is above disk end, the
|
||||||
|
* check if we overflow the total block limit is sufficient.
|
||||||
|
*/
|
||||||
|
if (disk_addr < amap_hint.addr)
|
||||||
|
update_amap_hint(amap + byte, disk_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the first free bit in one byte.
|
||||||
|
*/
|
||||||
|
static inline int find_first_empty_bit(u8 entry)
|
||||||
|
{
|
||||||
|
u8 i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
if (!(entry & 1 << (7 - i)))
|
||||||
|
return i;
|
||||||
|
/* unreachable */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the number of bytes addressed by one pointer entry for the
|
||||||
|
* specified level.
|
||||||
|
*/
|
||||||
|
static off_t bytes_per_level(int level)
|
||||||
|
{
|
||||||
|
off_t mult = BYTES_PER_BLOCK;
|
||||||
|
|
||||||
|
if (!level)
|
||||||
|
return 0;
|
||||||
|
level--;
|
||||||
|
while (level--)
|
||||||
|
mult *= PTRS_PER_BLOCK;
|
||||||
|
return mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_amap_entry_bit(off_t amap)
|
||||||
|
{
|
||||||
|
u8 entry;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = _read(&entry, sizeof(entry), amap);
|
||||||
|
BUG(rc < 0);
|
||||||
|
|
||||||
|
if (entry == 0xff)
|
||||||
|
return -1;
|
||||||
|
return find_first_empty_bit(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t __get_free_block_fast(void)
|
||||||
|
{
|
||||||
|
off_t addr, amap = amap_hint.amap_addr & ~DATA_BLOCK_MASK;
|
||||||
|
int bit, i = amap_hint.amap_addr & DATA_BLOCK_MASK;
|
||||||
|
|
||||||
|
for (; i < cmsfs.blksize; i++) {
|
||||||
|
bit = get_amap_entry_bit(amap + i);
|
||||||
|
if (bit == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Calculate the addr for the free block we've found. */
|
||||||
|
addr = (off_t) amap_blocknumber(amap_hint.addr) * BYTES_PER_BLOCK;
|
||||||
|
addr += i * 8 * cmsfs.blksize;
|
||||||
|
addr += bit * cmsfs.blksize;
|
||||||
|
|
||||||
|
amap_block_set(amap + i, bit);
|
||||||
|
update_amap_hint(amap + i, addr);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for the first unallocated block and return addr of allocated block.
|
||||||
|
*/
|
||||||
|
static off_t __get_free_block(int level, off_t amap, off_t addr)
|
||||||
|
{
|
||||||
|
off_t ptr;
|
||||||
|
int bit, i;
|
||||||
|
|
||||||
|
if (level > 0) {
|
||||||
|
for (i = 0; i < PTRS_PER_BLOCK; i++) {
|
||||||
|
ptr = get_fixed_pointer(amap);
|
||||||
|
if (!ptr)
|
||||||
|
return 0;
|
||||||
|
ptr = __get_free_block(level - 1, ptr,
|
||||||
|
addr + i * bytes_per_level(level));
|
||||||
|
if (ptr)
|
||||||
|
return ptr;
|
||||||
|
amap += PTR_SIZE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cmsfs.blksize; i++) {
|
||||||
|
bit = get_amap_entry_bit(amap + i);
|
||||||
|
if (bit == -1)
|
||||||
|
continue;
|
||||||
|
amap_block_set(amap + i, bit);
|
||||||
|
/* add byte offset */
|
||||||
|
addr += i * 8 * cmsfs.blksize;
|
||||||
|
/* add bit offset */
|
||||||
|
addr += bit * cmsfs.blksize;
|
||||||
|
update_amap_hint(amap + i, addr);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a free block and increment label block counter.
|
||||||
|
*/
|
||||||
|
off_t get_free_block(void)
|
||||||
|
{
|
||||||
|
off_t addr = 0;
|
||||||
|
|
||||||
|
if (cmsfs.used_blocks + cmsfs.reserved_blocks >= cmsfs.total_blocks)
|
||||||
|
return -ENOSPC;
|
||||||
|
if (amap_hint.amap_addr)
|
||||||
|
addr = __get_free_block_fast();
|
||||||
|
if (!addr)
|
||||||
|
addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0);
|
||||||
|
BUG(!addr);
|
||||||
|
|
||||||
|
cmsfs.used_blocks++;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a zero-filled block and increment label block counter.
|
||||||
|
*/
|
||||||
|
off_t get_zero_block(void)
|
||||||
|
{
|
||||||
|
off_t addr = get_free_block();
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (addr < 0)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
rc = _zero(addr, cmsfs.blksize);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a block and decrement label block counter.
|
||||||
|
*/
|
||||||
|
void free_block(off_t addr)
|
||||||
|
{
|
||||||
|
if (addr) {
|
||||||
|
amap_block_clear(addr);
|
||||||
|
cmsfs.used_blocks--;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
cmsfs-fuse/cmsfs-fuse.1
Normal file
208
cmsfs-fuse/cmsfs-fuse.1
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
.\" Copyright IBM Corp. 2010, 2017
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH CMSFS-FUSE 1 "February 2010" "s390-tools"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
cmsfs-fuse \- File system for z/VM CMS disks
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.SS mounting:
|
||||||
|
.TP
|
||||||
|
\fBcmsfs-fuse\fP DEVICE MOUNTPOINT [OPTIONS]
|
||||||
|
.SS unmounting:
|
||||||
|
.TP
|
||||||
|
\fBfusermount\fP -u MOUNTPOINT
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Use the \fBcmsfs-fuse\fP command to provide read and write access
|
||||||
|
to files stored on a z/VM CMS disk.
|
||||||
|
The cmsfs-fuse file system translates the record-based EDF file system on
|
||||||
|
the CMS disk to UNIX semantics.
|
||||||
|
After mounting the CMS disk, you can use common Linux tools to access
|
||||||
|
the files on the disk. You can enable automatic conversions of text files from
|
||||||
|
EBCDIC to ASCII.
|
||||||
|
|
||||||
|
Attention: You can inadvertently damage files and lose data when directly
|
||||||
|
writing to files within the cmsfs-fuse file system. To avoid problems when writing,
|
||||||
|
multiple restrictions must be observed, especially with regard to linefeeds (see
|
||||||
|
section RESTRICTIONS).
|
||||||
|
|
||||||
|
If you are unsure about how to safely write to a file on the cmsfs-fuse file
|
||||||
|
system, copy the file to a location outside the cmsfs-fuse file system, edit the file,
|
||||||
|
and then copy it back to its original location.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.SS "general options:"
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR opt,[opt...]
|
||||||
|
Fuse or mount command options. For fuse options see below, for mount options
|
||||||
|
see \fBmount(8)\fP.
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR or \fB\-\-help\fR
|
||||||
|
Print usage information, then exit.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR or \fB\-\-version\fR
|
||||||
|
Print version information, then exit.
|
||||||
|
.SS "cmsfs-fuse options:"
|
||||||
|
.TP
|
||||||
|
\fB\-a\fR or \fB\-\-ascii\fR
|
||||||
|
Interpret all files on the CMS disk as text files and convert them from
|
||||||
|
EBCDIC to ASCII.
|
||||||
|
.TP
|
||||||
|
\fB--from\fR
|
||||||
|
The codepage of the files on the CMS disk. If this option is not
|
||||||
|
specified the default codepage CP1047 is used. For a list of all available
|
||||||
|
codepages see iconv --list.
|
||||||
|
.TP
|
||||||
|
\fB--to\fR
|
||||||
|
The codepage to which CMS files should be converted to. If this option is not
|
||||||
|
specified the default codepage ISO-8859-1 is used. For a list of all available
|
||||||
|
codepages see iconv --list.
|
||||||
|
.TP
|
||||||
|
\fB\-t\fR or \fB\-\-filetype\fR
|
||||||
|
Interpret files on the CMS disk as text files based on the file type
|
||||||
|
and convert them from EBCDIC to ASCII. The file types that are treated
|
||||||
|
as text files are taken from a configuration file (see section CONFIGURATION FILES).
|
||||||
|
|
||||||
|
.SS "Applicable FUSE options (version 2.8):"
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR or \fB\-o\fR debug
|
||||||
|
Enable debug output (implies \fB\-f\fR)
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR
|
||||||
|
Foreground operation
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR allow_other
|
||||||
|
Allow access by other users
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR allow_root
|
||||||
|
Allow access by root
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR nonempty
|
||||||
|
Allow mounts over non\-empty file/dir
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR default_permissions
|
||||||
|
Enable permission checking by kernel
|
||||||
|
.TP
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR max_read=N
|
||||||
|
Set maximum size of read requests
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR kernel_cache
|
||||||
|
Cache files in kernel
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR [no]auto_cache
|
||||||
|
Enable caching based on modification times
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR umask=M
|
||||||
|
Set file permissions (octal)
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR uid=N
|
||||||
|
Set file owner
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR gid=N
|
||||||
|
Set file group
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR max_write=N
|
||||||
|
Set maximum size of write requests
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR max_readahead=N
|
||||||
|
Set maximum readahead
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR async_read
|
||||||
|
Perform reads asynchronously (default)
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR sync_read
|
||||||
|
Perform reads synchronously
|
||||||
|
.TP
|
||||||
|
\fB\-o big_writes\fR
|
||||||
|
Enable write operations with more than 4 KB
|
||||||
|
|
||||||
|
.SH EXTENDED ATTRIBUTES
|
||||||
|
Use the following extended attributes to handle the CMS characteristics of a file:
|
||||||
|
|
||||||
|
\fBuser.record_format\fR: The format of a file. Allowed values are F for fixed record length files
|
||||||
|
and V for variable record length files. This attribute can be set only if the file is empty.
|
||||||
|
|
||||||
|
\fBuser.record_lrecl\fR: The record length of a file. This attribute can be set only for a fixed
|
||||||
|
record length file and if the file is empty. A valid record length is an integer in the range 1-65535.
|
||||||
|
|
||||||
|
\fBuser.file_mode\fR: The file mode of a file which is interpreted by CMS. The file mode consists
|
||||||
|
of a mode letter from A-Z and mode number from 0-6.
|
||||||
|
|
||||||
|
New files are created by default as variable files with file mode A1.
|
||||||
|
|
||||||
|
.SH RESTRICTIONS
|
||||||
|
\fBrename\fR and \fBcreat\fR:
|
||||||
|
Uppercase file names are enforced.
|
||||||
|
|
||||||
|
\fBtruncate\fR:
|
||||||
|
Only shrinking of a file is supported. For fixed length record files, the new file size must
|
||||||
|
be a multiple of the record length.
|
||||||
|
|
||||||
|
\fBunlink\fR:
|
||||||
|
Creating a file with the name of a previously unlinked file which is still in use is not supported
|
||||||
|
and will fail with -ENOENT.
|
||||||
|
|
||||||
|
\fBwrite\fR:
|
||||||
|
Writes are supported only at the end of the file.
|
||||||
|
A write on a fixed length record file always writes a multiple
|
||||||
|
of the record length. If additional bytes are added, the
|
||||||
|
bytes are filled with zero in binary mode or with spaces in ASCII mode. Sparse files are not supported.
|
||||||
|
If the cp tool is used to write files to a CMS disk the option "--sparse=never" must be specified.
|
||||||
|
|
||||||
|
If ASCII translation is enabled for a file a linefeed character determines the end of a record.
|
||||||
|
The following restrictions must be observed for writing files in ASCII mode:
|
||||||
|
For fixed record length files a linefeed must occur exactly after a record of the length specified in the fixed record length.
|
||||||
|
For variable record length files a linefeed must occur after the maximum record length is reached or earlier.
|
||||||
|
If a record of a variable record length file consists only of a linefeed character cmsfs-fuse adds a space to this record since
|
||||||
|
empty records are not supported by the CMS file system.
|
||||||
|
|
||||||
|
.SH CONFIGURATION FILES
|
||||||
|
cmsfs-fuse uses a configuration file for automatic translation based on the file type.
|
||||||
|
Upon startup, cmsfs-fuse evaluates the file .cmsfs-fuse/filetypes.conf in the user's home directory. If the file does not
|
||||||
|
exist cmsfs-fuse evaluates the file /etc/cmsfs-fuse/filetypes.conf.
|
||||||
|
|
||||||
|
The filetypes.conf file contains the CMS file types that are automaticaly translated to ASCII if cmsfs-fuse is started
|
||||||
|
with the -t option. The syntax of the configuration file is one file type per line. Lines that start with a # followed by a space are treated as
|
||||||
|
comments and are ignored. The file type is 8 characters long and must consist of valid CMS file name characters only.
|
||||||
|
|
||||||
|
The default file types in the configuration file were taken from the z/VM TCPIP.DATA file
|
||||||
|
(z/VM version 5.4.0).
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
To mount the CMS disk with the name dasde enter:
|
||||||
|
.br
|
||||||
|
|
||||||
|
# cmsfs-fuse /dev/dasde /mnt
|
||||||
|
|
||||||
|
.br
|
||||||
|
To mount the CMS disk with the name dasde and enable automatic translation
|
||||||
|
of known text files enter:
|
||||||
|
.br
|
||||||
|
|
||||||
|
# cmsfs-fuse -t /dev/dasde /mnt
|
||||||
|
|
||||||
|
To mount the CMS disk with the name dasde and enable automatic translation
|
||||||
|
of all files to UTF-8 enter:
|
||||||
|
.br
|
||||||
|
|
||||||
|
# cmsfs-fuse --to=UTF-8 -a /dev/dasde /mnt
|
||||||
|
|
||||||
|
To unmount the CMS disk mounted on /mnt enter:
|
||||||
|
.br
|
||||||
|
|
||||||
|
# fusermount -u /mnt
|
||||||
|
|
||||||
|
To show the record format of file PROFILE.EXEC assuming the CMS disk was mounted on /mnt:
|
||||||
|
|
||||||
|
# getfattr -n user.record_format /mnt/PROFILE.EXEC
|
||||||
|
|
||||||
|
The following example assumes that an empty, fixed record format file, PROFILE.EXEC, can be accessed on a CMS disk that has been mounted on /mnt. To set the record length of PROFILE.EXEC to 80 bytes:
|
||||||
|
|
||||||
|
# setfattr -n user.record_lrecl -v 80 /mnt/PROFILE.EXEC
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
attr (5), getfattr (1), setfattr(1), iconv(1) and Linux on System z: Device Drivers, Features and Commands
|
||||||
4646
cmsfs-fuse/cmsfs-fuse.c
Normal file
4646
cmsfs-fuse/cmsfs-fuse.c
Normal file
File diff suppressed because it is too large
Load Diff
132
cmsfs-fuse/cmsfs-fuse.h
Normal file
132
cmsfs-fuse/cmsfs-fuse.h
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* Data structures
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CMSFS_H
|
||||||
|
#define _CMSFS_H
|
||||||
|
|
||||||
|
#include <iconv.h>
|
||||||
|
#include <search.h>
|
||||||
|
|
||||||
|
#include "lib/util_list.h"
|
||||||
|
|
||||||
|
#define COMP "cmsfs-fuse: "
|
||||||
|
extern struct cmsfs cmsfs;
|
||||||
|
|
||||||
|
/* conversion between absolute and relative addresses */
|
||||||
|
#define ABS(x) ((off_t) (x - 1) * cmsfs.blksize)
|
||||||
|
#define REL(x) ((x / cmsfs.blksize) + 1)
|
||||||
|
|
||||||
|
struct fcache_entry {
|
||||||
|
/* filename used as hash key */
|
||||||
|
char name[18];
|
||||||
|
/* location of fst entry */
|
||||||
|
off_t fst_addr;
|
||||||
|
/* filename string address */
|
||||||
|
char *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum cmsfs_mode {
|
||||||
|
BINARY_MODE,
|
||||||
|
TEXT_MODE,
|
||||||
|
TYPE_MODE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* the per device global struture */
|
||||||
|
struct cmsfs {
|
||||||
|
/* name of the block device, e.g. /dev/dasde */
|
||||||
|
const char *device;
|
||||||
|
/* global file descriptor of the underlying block device */
|
||||||
|
int fd;
|
||||||
|
/* start of mmap of the whole block device */
|
||||||
|
char *map;
|
||||||
|
/* size of the disk */
|
||||||
|
off_t size;
|
||||||
|
/* formatted blocksize */
|
||||||
|
int blksize;
|
||||||
|
/* number of 512 byte blocks per block */
|
||||||
|
int nr_blocks_512;
|
||||||
|
/* device is read only */
|
||||||
|
int readonly;
|
||||||
|
/* access permission for other users */
|
||||||
|
int allow_other;
|
||||||
|
/* offset to label */
|
||||||
|
off_t label;
|
||||||
|
/* offset to file directory root FST */
|
||||||
|
off_t fdir;
|
||||||
|
/* offset to allocation map */
|
||||||
|
off_t amap;
|
||||||
|
/* depth of directories */
|
||||||
|
int dir_levels;
|
||||||
|
/* depth of allocation maps */
|
||||||
|
int amap_levels;
|
||||||
|
/* files count on the device */
|
||||||
|
int files;
|
||||||
|
/* conversion mode */
|
||||||
|
enum cmsfs_mode mode;
|
||||||
|
/* iconv codepage options */
|
||||||
|
const char *codepage_from;
|
||||||
|
const char *codepage_to;
|
||||||
|
iconv_t iconv_from;
|
||||||
|
iconv_t iconv_to;
|
||||||
|
|
||||||
|
/* disk stats */
|
||||||
|
int total_blocks;
|
||||||
|
int used_blocks;
|
||||||
|
/* blocks reserved for outstanding meta data */
|
||||||
|
int reserved_blocks;
|
||||||
|
|
||||||
|
/* constants */
|
||||||
|
int fixed_ptrs_per_block;
|
||||||
|
int var_ptrs_per_block;
|
||||||
|
int bits_per_data_block;
|
||||||
|
int bits_per_ptr_block;
|
||||||
|
int data_block_mask;
|
||||||
|
off_t amap_bytes_per_block;
|
||||||
|
|
||||||
|
/* file cache */
|
||||||
|
struct fcache_entry *fcache;
|
||||||
|
int fcache_used;
|
||||||
|
int fcache_max;
|
||||||
|
struct hsearch_data htab;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_TYPE_LEN 9
|
||||||
|
|
||||||
|
struct filetype {
|
||||||
|
char name[MAX_TYPE_LEN];
|
||||||
|
struct util_list_node list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NULL_BLOCK 0
|
||||||
|
#define VAR_FILE_END 1
|
||||||
|
|
||||||
|
#define PTRS_PER_BLOCK (cmsfs.fixed_ptrs_per_block)
|
||||||
|
#define VPTRS_PER_BLOCK (cmsfs.var_ptrs_per_block)
|
||||||
|
#define DATA_BLOCK_MASK (cmsfs.data_block_mask)
|
||||||
|
#define BITS_PER_DATA_BLOCK (cmsfs.bits_per_data_block)
|
||||||
|
#define BYTES_PER_BLOCK (cmsfs.amap_bytes_per_block)
|
||||||
|
|
||||||
|
extern int get_device_info(struct cmsfs *cmsfs);
|
||||||
|
extern int scan_conf_file(struct util_list *list);
|
||||||
|
extern int is_edf_char(int c);
|
||||||
|
|
||||||
|
#ifndef _CMSFS_FSCK
|
||||||
|
int _read(void *, size_t, off_t);
|
||||||
|
int _write(const void *, size_t, off_t);
|
||||||
|
int _zero(off_t, size_t);
|
||||||
|
off_t get_fixed_pointer(off_t);
|
||||||
|
|
||||||
|
off_t get_free_block(void);
|
||||||
|
off_t get_zero_block(void);
|
||||||
|
void free_block(off_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
127
cmsfs-fuse/config.c
Normal file
127
cmsfs-fuse/config.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* Config option parsing
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#include "cmsfs-fuse.h"
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
#define MAX_LINE_LEN 80
|
||||||
|
|
||||||
|
static char *conf_file;
|
||||||
|
|
||||||
|
static int open_conf_file(FILE **fh)
|
||||||
|
{
|
||||||
|
const char *home_env;
|
||||||
|
|
||||||
|
conf_file = malloc(4096);
|
||||||
|
if (conf_file == NULL)
|
||||||
|
DIE_PERROR("malloc failed");
|
||||||
|
|
||||||
|
home_env = getenv("HOME");
|
||||||
|
if (home_env == NULL)
|
||||||
|
goto no_home;
|
||||||
|
|
||||||
|
sprintf(conf_file, "%s/.cmsfs-fuse/filetypes.conf", home_env);
|
||||||
|
*fh = fopen(conf_file, "r");
|
||||||
|
if (*fh != NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
no_home:
|
||||||
|
sprintf(conf_file, "%s/%s", TOOLS_SYSCONFDIR,
|
||||||
|
"/cmsfs-fuse/filetypes.conf");
|
||||||
|
*fh = fopen(conf_file, "r");
|
||||||
|
if (*fh == NULL) {
|
||||||
|
free(conf_file);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
DEBUG("using config file: %s\n", conf_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_filetype(char *name, struct util_list *list)
|
||||||
|
{
|
||||||
|
struct filetype *entry;
|
||||||
|
|
||||||
|
entry = malloc(sizeof(*entry));
|
||||||
|
if (entry == NULL)
|
||||||
|
DIE_PERROR("malloc failed");
|
||||||
|
strncpy(entry->name, name, MAX_TYPE_LEN);
|
||||||
|
util_list_add_head(list, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int filetype_valid(const char *type, int line)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (strlen(type) > 8) {
|
||||||
|
WARN("entry too long in line: %d in config file: %s\n",
|
||||||
|
line, conf_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < strlen(type); i++)
|
||||||
|
if (!is_edf_char(*(type + i))) {
|
||||||
|
WARN("invalid character in line: %d in config file: %s\n",
|
||||||
|
line, conf_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scan_conf_file(struct util_list *list)
|
||||||
|
{
|
||||||
|
char buf[MAX_LINE_LEN], *tmp;
|
||||||
|
int line = 0;
|
||||||
|
FILE *fh;
|
||||||
|
|
||||||
|
if (open_conf_file(&fh) < 0)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
while (fgets(buf, MAX_LINE_LEN, fh) != NULL) {
|
||||||
|
line++;
|
||||||
|
tmp = buf;
|
||||||
|
while (isblank(*tmp))
|
||||||
|
tmp++;
|
||||||
|
|
||||||
|
if (*tmp == '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip comments, comment must be "# " because # is a valid
|
||||||
|
* EDF character.
|
||||||
|
*/
|
||||||
|
if (strlen(tmp) > 1 && *tmp == '#' && *(tmp + 1) == ' ')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* remove trailing \n */
|
||||||
|
if (strlen(tmp) && *(tmp + strlen(tmp) - 1) == '\n')
|
||||||
|
*(tmp + strlen(tmp) - 1) = '\0';
|
||||||
|
|
||||||
|
if (filetype_valid(tmp, line))
|
||||||
|
add_filetype(tmp, list);
|
||||||
|
}
|
||||||
|
fclose(fh);
|
||||||
|
free(conf_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
180
cmsfs-fuse/dasd.c
Normal file
180
cmsfs-fuse/dasd.c
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* DASD specific functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "cmsfs-fuse.h"
|
||||||
|
#include "edf.h"
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
#define BLKSSZGET _IO(0x12, 104)
|
||||||
|
|
||||||
|
/* CMS disk label starts with ASCII string "CMS1" */
|
||||||
|
#define VOL_LABEL_EBCDIC 0xc3d4e2f1
|
||||||
|
|
||||||
|
static int disk_supported(int fd, struct cmsfs *cmsfs)
|
||||||
|
{
|
||||||
|
unsigned int cms_id = VOL_LABEL_EBCDIC;
|
||||||
|
struct cms_label label;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = lseek(fd, cmsfs->label, SEEK_SET);
|
||||||
|
if (rc < 0) {
|
||||||
|
perror(COMP "lseek failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = read(fd, &label, sizeof(label));
|
||||||
|
if (rc < 0) {
|
||||||
|
perror(COMP "read failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that the label contains the CMS1 string */
|
||||||
|
if (memcmp(label.id, &cms_id, sizeof(cms_id)) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* label sanity checks */
|
||||||
|
if (label.blocksize != 4096 &&
|
||||||
|
label.blocksize != 2048 &&
|
||||||
|
label.blocksize != 1024 &&
|
||||||
|
label.blocksize != 512) {
|
||||||
|
fprintf(stderr, COMP "Invalid disk block size!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.dop != 4 && label.dop != 5) {
|
||||||
|
fprintf(stderr, COMP "Invalid disk origin pointer!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.fst_entry_size != sizeof(struct fst_entry)) {
|
||||||
|
fprintf(stderr, COMP "Invalid FST entry size!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.fst_per_block != label.blocksize / label.fst_entry_size) {
|
||||||
|
fprintf(stderr, COMP "Invalid FST per block value!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the blocksize to the formatted one */
|
||||||
|
cmsfs->blksize = label.blocksize;
|
||||||
|
DEBUG(" DOP: %d", label.dop);
|
||||||
|
/* block number 5 means 0x4000... */
|
||||||
|
cmsfs->fdir = (label.dop - 1) * cmsfs->blksize;
|
||||||
|
DEBUG(" fdir: %lx", cmsfs->fdir);
|
||||||
|
/* get disk usage for statfs */
|
||||||
|
cmsfs->total_blocks = label.total_blocks;
|
||||||
|
cmsfs->used_blocks = label.used_blocks;
|
||||||
|
DEBUG(" Total blocks: %d Used blocks: %d",
|
||||||
|
cmsfs->total_blocks, cmsfs->used_blocks);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_device_info_ioctl(int fd, struct cmsfs *cmsfs)
|
||||||
|
{
|
||||||
|
if (ioctl(fd, BLKSSZGET, &cmsfs->blksize) != 0)
|
||||||
|
DIE("ioctl error get blocksize\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int label_offsets[] = { 4096, 512, 2048, 1024, 8192 };
|
||||||
|
|
||||||
|
static void get_device_info_file(int fd, struct cmsfs *cmsfs)
|
||||||
|
{
|
||||||
|
unsigned int cms_id = VOL_LABEL_EBCDIC;
|
||||||
|
unsigned int i;
|
||||||
|
char label[4];
|
||||||
|
off_t offset;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
cmsfs->label = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the blocksize from label. Unfortunately the blocksize
|
||||||
|
* position depends on the blocksize... time for some heuristics.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ARRAY_SIZE(label_offsets); i++) {
|
||||||
|
offset = label_offsets[i];
|
||||||
|
|
||||||
|
rc = lseek(fd, offset, SEEK_SET);
|
||||||
|
if (rc < 0)
|
||||||
|
DIE_PERROR("lseek failed");
|
||||||
|
|
||||||
|
rc = read(fd, &label, 4);
|
||||||
|
if (rc < 0)
|
||||||
|
DIE_PERROR("read failed");
|
||||||
|
|
||||||
|
/* check if the label contains the CMS1 string */
|
||||||
|
if (memcmp(label, &cms_id, sizeof(cms_id)) == 0) {
|
||||||
|
cmsfs->label = offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cmsfs->label)
|
||||||
|
DIE("Error CMS1 label not found!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_device_info(struct cmsfs *cmsfs)
|
||||||
|
{
|
||||||
|
struct stat stat;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open writable, if write access is not granted fall back to
|
||||||
|
* read only.
|
||||||
|
*/
|
||||||
|
fd = open(cmsfs->device, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (errno == EROFS || errno == EACCES) {
|
||||||
|
cmsfs->readonly = 1;
|
||||||
|
fd = open(cmsfs->device, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
DIE_PERROR("open failed");
|
||||||
|
} else
|
||||||
|
DIE_PERROR("open failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat(fd, &stat) < 0)
|
||||||
|
DIE_PERROR("fstat failed");
|
||||||
|
|
||||||
|
if (S_ISBLK(stat.st_mode)) {
|
||||||
|
get_device_info_ioctl(fd, cmsfs);
|
||||||
|
cmsfs->label = 2 * cmsfs->blksize;
|
||||||
|
|
||||||
|
/* FBA disks have a different label location */
|
||||||
|
if (!disk_supported(fd, cmsfs)) {
|
||||||
|
cmsfs->label = cmsfs->blksize;
|
||||||
|
if (!disk_supported(fd, cmsfs))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if (S_ISREG(stat.st_mode))
|
||||||
|
get_device_info_file(fd, cmsfs);
|
||||||
|
else
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!disk_supported(fd, cmsfs))
|
||||||
|
goto error;
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
error:
|
||||||
|
DIE("Unsupported disk\n");
|
||||||
|
}
|
||||||
156
cmsfs-fuse/ebcdic.h
Normal file
156
cmsfs-fuse/ebcdic.h
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* EBCDIC to ASCII conversion:
|
||||||
|
* EDF uses an EBCDIC codepage based on 037 with some modifications.
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _EBCDIC_H
|
||||||
|
#define _EBCDIC_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EBCDIC 037 -> ISO8859-1
|
||||||
|
* changes:
|
||||||
|
* 0x5f: 0xaa -> 0x5e ^
|
||||||
|
* 0xad: 0x07 -> 0x5b [
|
||||||
|
* 0xbd: 0x07 -> 0x5d ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char ebc2asc[256] = {
|
||||||
|
/* 0x00 */
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||||
|
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
/* 0x10 */
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
||||||
|
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
/* 0x20 */
|
||||||
|
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
||||||
|
/* 0x30 */
|
||||||
|
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
||||||
|
/* 0x40 */
|
||||||
|
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
||||||
|
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
||||||
|
/* 0x50 */
|
||||||
|
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
||||||
|
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
|
||||||
|
/* 0x60 */
|
||||||
|
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
||||||
|
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
||||||
|
/* 0x70 */
|
||||||
|
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
||||||
|
/* 0x80 */
|
||||||
|
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||||
|
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
||||||
|
/* 0x90 */
|
||||||
|
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
||||||
|
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
||||||
|
/* 0xa0 */
|
||||||
|
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||||
|
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x5B, 0x07, 0x07,
|
||||||
|
/* 0xb0 */
|
||||||
|
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
||||||
|
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x5D, 0x07, 0x07,
|
||||||
|
/* 0xc0 */
|
||||||
|
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||||||
|
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
||||||
|
/* 0xd0 */
|
||||||
|
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||||
|
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
||||||
|
/* 0xe0 */
|
||||||
|
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||||
|
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
||||||
|
/* 0xf0 */
|
||||||
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||||
|
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ISO8859-1 -> EBCDIC 037 */
|
||||||
|
static char asc2ebc[256] = {
|
||||||
|
/* 0x00 */
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
|
||||||
|
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
/* 0x10 */
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
|
||||||
|
0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
|
||||||
|
/* 0x20 */
|
||||||
|
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
|
||||||
|
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
|
||||||
|
/* 0x30 */
|
||||||
|
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
|
||||||
|
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
|
||||||
|
/* 0x40 */
|
||||||
|
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
|
||||||
|
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
|
||||||
|
/* 0x50 */
|
||||||
|
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
|
||||||
|
0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D,
|
||||||
|
/* 0x60 */
|
||||||
|
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||||
|
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||||
|
/* 0x70 */
|
||||||
|
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
|
||||||
|
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
|
||||||
|
/* 0x80 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0x90 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xa0 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xb0 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xc0 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xd0 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xe0 */
|
||||||
|
0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
/* 0xf0 */
|
||||||
|
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||||
|
0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EBCDIC_ENCODE 0x0
|
||||||
|
#define EBCDIC_DECODE 0x1
|
||||||
|
|
||||||
|
static inline void a2e(char *dst, const char *src, int len, int to)
|
||||||
|
{
|
||||||
|
char *conv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (to == EBCDIC_ENCODE)
|
||||||
|
conv = asc2ebc;
|
||||||
|
else
|
||||||
|
conv = ebc2asc;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
dst[i] = conv[(unsigned int)src[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ebcdic_enc(char *dst, const char *src, int len)
|
||||||
|
{
|
||||||
|
a2e(dst, src, len, EBCDIC_ENCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ebcdic_dec(char *dst, const char *src, int len)
|
||||||
|
{
|
||||||
|
a2e(dst, src, len, EBCDIC_DECODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
125
cmsfs-fuse/edf.h
Normal file
125
cmsfs-fuse/edf.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* EDF and label structures
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _EDF_H
|
||||||
|
#define _EDF_H
|
||||||
|
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File status table entry
|
||||||
|
*/
|
||||||
|
struct fst_entry {
|
||||||
|
char name[8];
|
||||||
|
char type[8];
|
||||||
|
char res1[8];
|
||||||
|
|
||||||
|
short int mode;
|
||||||
|
char res2[4];
|
||||||
|
|
||||||
|
char record_format;
|
||||||
|
char flag;
|
||||||
|
int record_len;
|
||||||
|
char res3[4];
|
||||||
|
|
||||||
|
unsigned int fop;
|
||||||
|
/* number of data blocks (not incl. pointer blocks) */
|
||||||
|
int nr_blocks;
|
||||||
|
int nr_records;
|
||||||
|
char levels;
|
||||||
|
char ptr_size;
|
||||||
|
char date[6];
|
||||||
|
char res4[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cms_label {
|
||||||
|
char id[6];
|
||||||
|
char user_id[6];
|
||||||
|
|
||||||
|
unsigned int blocksize;
|
||||||
|
unsigned int dop;
|
||||||
|
unsigned int f_cylinders;
|
||||||
|
unsigned int max_cylinders;
|
||||||
|
|
||||||
|
unsigned int total_blocks;
|
||||||
|
unsigned int used_blocks;
|
||||||
|
|
||||||
|
unsigned int fst_entry_size;
|
||||||
|
unsigned int fst_per_block;
|
||||||
|
|
||||||
|
char date[6];
|
||||||
|
unsigned int res1[3];
|
||||||
|
char res2[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RECORD_LEN_VARIABLE 0xe5
|
||||||
|
#define RECORD_LEN_FIXED 0xc6
|
||||||
|
|
||||||
|
/* TODO: correct for fixed? */
|
||||||
|
#define MAX_RECORD_LEN 0xffff
|
||||||
|
|
||||||
|
#define FST_ENTRY_SIZE sizeof(struct fst_entry)
|
||||||
|
#define FST_ENTRY_DIR_NAME 0x0000000100000000ULL
|
||||||
|
#define FST_ENTRY_DIR_TYPE 0xc4c9d9c5c3e3d6d9ULL /* 'DIRECTOR' */
|
||||||
|
#define FST_ENTRY_ALLOC_NAME 0x0000000200000000ULL
|
||||||
|
#define FST_ENTRY_ALLOC_TYPE 0xc1d3d3d6c3d4c1d7ULL /* 'ALLOCMAP' */
|
||||||
|
|
||||||
|
#define FST_FLAG_CENTURY 0x0008
|
||||||
|
#define FST_FOP_OFFSET 0x28
|
||||||
|
#define FST_LEVEL_OFFSET 0x34
|
||||||
|
|
||||||
|
#define VAR_RECORD_HEADER_SIZE 0x2
|
||||||
|
#define VAR_RECORD_SPANNED 0xffffffff
|
||||||
|
|
||||||
|
#define PTR_SIZE (sizeof(struct fixed_ptr))
|
||||||
|
#define VPTR_SIZE (sizeof(struct var_ptr))
|
||||||
|
|
||||||
|
struct fixed_ptr {
|
||||||
|
unsigned int next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct var_ptr {
|
||||||
|
unsigned int next;
|
||||||
|
int hi_record_nr;
|
||||||
|
unsigned int disp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int is_directory(const char *name,
|
||||||
|
const char *type)
|
||||||
|
{
|
||||||
|
if ((*(unsigned long long *) name) != FST_ENTRY_DIR_NAME)
|
||||||
|
return 0;
|
||||||
|
if ((*(unsigned long long *) type) != FST_ENTRY_DIR_TYPE)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_allocmap(const char *name,
|
||||||
|
const char *type)
|
||||||
|
{
|
||||||
|
if ((*(unsigned long long *) name) != FST_ENTRY_ALLOC_NAME)
|
||||||
|
return 0;
|
||||||
|
if ((*(unsigned long long *) type) != FST_ENTRY_ALLOC_TYPE)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_file(void *name, void *type)
|
||||||
|
{
|
||||||
|
if ((*(unsigned long long *) name) == 0ULL)
|
||||||
|
return 0;
|
||||||
|
/* Assumption: type = 0 is not legal */
|
||||||
|
if ((*(unsigned long long *) type) == 0ULL)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
107
cmsfs-fuse/etc/filetypes.conf
Normal file
107
cmsfs-fuse/etc/filetypes.conf
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#
|
||||||
|
# Filetypes that are interpreted as text files. If you want an EBCDIC
|
||||||
|
# file translated to ASCII, add the extension here.
|
||||||
|
#
|
||||||
|
# Comments must include a space after the #
|
||||||
|
|
||||||
|
# Add your extensions here:
|
||||||
|
PRM
|
||||||
|
CONF
|
||||||
|
|
||||||
|
# The following types were taken from the z/VM TCPIP.DATA file:
|
||||||
|
$EXEC
|
||||||
|
$REXX
|
||||||
|
$XEDIT
|
||||||
|
AMS
|
||||||
|
AMSERV
|
||||||
|
ANN
|
||||||
|
ANNOUNCE
|
||||||
|
APP
|
||||||
|
APPEND
|
||||||
|
ASC
|
||||||
|
ASCII
|
||||||
|
ASM
|
||||||
|
ASM3705
|
||||||
|
ASSEMBLE
|
||||||
|
AVL
|
||||||
|
AVAIL
|
||||||
|
A37
|
||||||
|
BASDATA
|
||||||
|
BASIC
|
||||||
|
BKS
|
||||||
|
BKSHELF
|
||||||
|
C
|
||||||
|
C++
|
||||||
|
CAT
|
||||||
|
CATALOG
|
||||||
|
CNTRL
|
||||||
|
COB
|
||||||
|
COBOL
|
||||||
|
COPY
|
||||||
|
CPP
|
||||||
|
DIRECT
|
||||||
|
DLCS
|
||||||
|
DOCUMENT
|
||||||
|
ESERV
|
||||||
|
EXC
|
||||||
|
EXEC
|
||||||
|
FFT
|
||||||
|
FOR
|
||||||
|
FORM
|
||||||
|
FORTRAN
|
||||||
|
FREEFORT
|
||||||
|
GCS
|
||||||
|
GROUP
|
||||||
|
H
|
||||||
|
HPP
|
||||||
|
HTM
|
||||||
|
HTML
|
||||||
|
H++
|
||||||
|
JOB
|
||||||
|
LISTING
|
||||||
|
LOG
|
||||||
|
LST
|
||||||
|
MAC
|
||||||
|
MACLIB
|
||||||
|
MACRO
|
||||||
|
MAK
|
||||||
|
MAKE
|
||||||
|
ME
|
||||||
|
MEMBER
|
||||||
|
MEMO
|
||||||
|
MODULE
|
||||||
|
NAM
|
||||||
|
NAMES
|
||||||
|
NETLOG
|
||||||
|
NONE
|
||||||
|
NOT
|
||||||
|
NOTE
|
||||||
|
NOTEBOOK
|
||||||
|
OFS
|
||||||
|
OPT
|
||||||
|
OPTIONS
|
||||||
|
PACKAGE
|
||||||
|
PASCAL
|
||||||
|
PKG
|
||||||
|
PLAS
|
||||||
|
PLI
|
||||||
|
PLIOPT
|
||||||
|
PLS
|
||||||
|
PVT
|
||||||
|
REXX
|
||||||
|
RPG
|
||||||
|
SCR
|
||||||
|
SCRIPT
|
||||||
|
STY
|
||||||
|
STYLE
|
||||||
|
TEXT
|
||||||
|
TEXTXXXX
|
||||||
|
TXT
|
||||||
|
TXTXXXX
|
||||||
|
UPDATE
|
||||||
|
UPDT
|
||||||
|
VMT
|
||||||
|
VSBASIC
|
||||||
|
VSBDATA
|
||||||
|
XED
|
||||||
|
XEDIT
|
||||||
54
cmsfs-fuse/helper.h
Normal file
54
cmsfs-fuse/helper.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* cmsfs-fuse - CMS EDF filesystem support for Linux
|
||||||
|
*
|
||||||
|
* Common helper functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2010, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HELPER_H
|
||||||
|
#define _HELPER_H
|
||||||
|
|
||||||
|
extern FILE *logfile;
|
||||||
|
#define DEBUG_LOGFILE "/tmp/cmsfs-fuse.log"
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
#define DEBUG(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(logfile, __VA_ARGS__); \
|
||||||
|
fflush(logfile); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define DEBUG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DIE(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, COMP __VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DIE_PERROR(...) \
|
||||||
|
do { \
|
||||||
|
perror(COMP __VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define BUG(x) \
|
||||||
|
if (x) { \
|
||||||
|
fprintf(stderr, COMP " assert failed at " \
|
||||||
|
__FILE__ ":%d in %s()\n", __LINE__, __func__); \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WARN(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, COMP "Warning, " __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
|
#endif
|
||||||
353
common.mak
Normal file
353
common.mak
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
ifndef $(COMMON_INCLUDED)
|
||||||
|
COMMON_INCLUDED = true
|
||||||
|
|
||||||
|
# Global definitions
|
||||||
|
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||||
|
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||||
|
VERSION = 1
|
||||||
|
RELEASE = 39
|
||||||
|
PATCHLEVEL = 0
|
||||||
|
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||||
|
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||||
|
export S390_TOOLS_RELEASE
|
||||||
|
|
||||||
|
reldir = $(subst $(shell cd -P $(dir $(filter %common.mak,$(MAKEFILE_LIST))); \
|
||||||
|
pwd)/,,$(CURDIR))
|
||||||
|
rootdir= $(dir $(filter %common.mak,$(MAKEFILE_LIST)))
|
||||||
|
export S390_TEST_LIB_PATH=$(rootdir)/s390-tools-testsuite/lib
|
||||||
|
|
||||||
|
#
|
||||||
|
# For cross compiles specify CROSS_COMPILE= on the commandline:
|
||||||
|
#
|
||||||
|
# $ make CROSS_COMPILE="s390x-5.1.0-"
|
||||||
|
#
|
||||||
|
|
||||||
|
CROSS_COMPILE =
|
||||||
|
|
||||||
|
#
|
||||||
|
# Commands can be overwritten on the command line with "make <VAR>=<VALUE>":
|
||||||
|
#
|
||||||
|
# $ make CC=gcc-4.8
|
||||||
|
#
|
||||||
|
# The "cmd_define" macro wraps the command definition so that the commands
|
||||||
|
# can be user supplied and are still pretty-printed for the build process.
|
||||||
|
#
|
||||||
|
# The macro is called with the following parameters:
|
||||||
|
#
|
||||||
|
# $(1) - Used command variable in the Makefiles
|
||||||
|
# $(2) - Pretty Print output for the command
|
||||||
|
# $(3) - Default command if not user-specified
|
||||||
|
#
|
||||||
|
# The Example below...
|
||||||
|
#
|
||||||
|
# $(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
||||||
|
#
|
||||||
|
# ... produces the following code:
|
||||||
|
#
|
||||||
|
# CC = $(CROSS_COMPILE)gcc
|
||||||
|
# CC_SILENT := $(CC)
|
||||||
|
# override CC = $(call echocmd," CC ",/$@)$(CC_SILENT)
|
||||||
|
#
|
||||||
|
# The "strip" make function is used for the first parameter to allow blanks,
|
||||||
|
# which improves readability.
|
||||||
|
#
|
||||||
|
|
||||||
|
define cmd_define
|
||||||
|
$(strip $(1)) = $(3)
|
||||||
|
$(strip $(1))_SILENT := $$($(strip $(1)))
|
||||||
|
override $(strip $(1)) = $$(call echocmd,$(2),/$$@)$$($(strip $(1))_SILENT)
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
|
||||||
|
$(eval $(call cmd_define, LINK," LINK ",$(CROSS_COMPILE)gcc))
|
||||||
|
$(eval $(call cmd_define, LD," LD ",$(CROSS_COMPILE)ld))
|
||||||
|
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
||||||
|
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
|
||||||
|
$(eval $(call cmd_define, LINKXX," LINKXX ",$(CROSS_COMPILE)g++))
|
||||||
|
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
|
||||||
|
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
|
||||||
|
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
|
||||||
|
$(eval $(call cmd_define, NM," NM ",$(CROSS_COMPILE)nm))
|
||||||
|
$(eval $(call cmd_define, STRIP," STRIP ",$(CROSS_COMPILE)strip))
|
||||||
|
$(eval $(call cmd_define,OBJCOPY," OBJCOPY ",$(CROSS_COMPILE)objcopy))
|
||||||
|
$(eval $(call cmd_define,OBJDUMP," OBJDUMP ",$(CROSS_COMPILE)objdump))
|
||||||
|
$(eval $(call cmd_define,RUNTEST," RUNTEST ",$(S390_TEST_LIB_PATH)/s390_runtest))
|
||||||
|
$(eval $(call cmd_define, CAT," CAT ",cat))
|
||||||
|
$(eval $(call cmd_define, SED," SED ",sed))
|
||||||
|
$(eval $(call cmd_define, GZIP," GZIP ",gzip))
|
||||||
|
$(eval $(call cmd_define, MV," MV ",mv))
|
||||||
|
|
||||||
|
CHECK = sparse
|
||||||
|
CHECK_SILENT := $(CHECK)
|
||||||
|
CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
|
||||||
|
|
||||||
|
SKIP = echo " SKIP $(call reldir) due to"
|
||||||
|
|
||||||
|
INSTALL = install
|
||||||
|
CP = cp
|
||||||
|
ifneq ("${V}","1")
|
||||||
|
MAKEFLAGS += --quiet
|
||||||
|
echocmd=echo $1$(call reldir)$2;
|
||||||
|
RUNTEST += > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
echocmd=
|
||||||
|
endif
|
||||||
|
ifeq ("${W}","1")
|
||||||
|
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security -Wextra
|
||||||
|
else
|
||||||
|
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
|
||||||
|
endif
|
||||||
|
ifeq ("${D}","1")
|
||||||
|
DEFAULT_CFLAGS += -Og
|
||||||
|
else
|
||||||
|
DEFAULT_CFLAGS += -O3
|
||||||
|
endif
|
||||||
|
|
||||||
|
DEFAULT_CPPFLAGS = -D_GNU_SOURCE
|
||||||
|
DEFAULT_LDFLAGS = -rdynamic
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check for build dependency
|
||||||
|
#
|
||||||
|
# $1: Name of tool or feature that requires dependency
|
||||||
|
# $2: Name of include file to check
|
||||||
|
# $3: Name of required devel package
|
||||||
|
# $4: Option to skip build (e.g. HAVE_FUSE=0)
|
||||||
|
#
|
||||||
|
check_dep=\
|
||||||
|
printf "\#include <%s>" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) -c -o /dev/null -xc - ) > /dev/null 2>&1; \
|
||||||
|
if [ $$? != 0 ]; \
|
||||||
|
then \
|
||||||
|
printf " REQCHK %s (%s)\n" $1 $2; \
|
||||||
|
printf "********************************************************************************\n" >&2; \
|
||||||
|
printf "* Missing build requirement for: %-45s *\n" $1 >&2; \
|
||||||
|
printf "* Install package..............: %-45s *\n" $3 >&2; \
|
||||||
|
printf "* You can skip build with......: make %-40s *\n" $4 >&2; \
|
||||||
|
printf "********************************************************************************\n" >&2; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Support alternate install root
|
||||||
|
#
|
||||||
|
# INSTALLDIR: Finally install s390-tools to INSTALLDIR. This can be used
|
||||||
|
# for testing locally installed tools.
|
||||||
|
# DESTDIR: Temporary install s390-tools to this directory. This can be
|
||||||
|
# used for building s390-tools e.g. with rpmbuild.
|
||||||
|
#
|
||||||
|
# The difference between INSTALLDIR and DESTDIR is that for INSTALLDIR
|
||||||
|
# internally used directories (e.g. for config files) are adjusted.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# $ cd cpumf
|
||||||
|
# $ INSTALLDIR=/tmp make install
|
||||||
|
# $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR
|
||||||
|
# my $CPUMF_DATA_DIR = '/tmp/usr/share/s390-tools/cpumf';
|
||||||
|
#
|
||||||
|
# $ make clean
|
||||||
|
# $ DESTDIR=/tmp make install
|
||||||
|
# $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR
|
||||||
|
# my $CPUMF_DATA_DIR = '/usr/share/s390-tools/cpumf';
|
||||||
|
#
|
||||||
|
|
||||||
|
ifdef INSTROOT
|
||||||
|
$(error INSTROOT is no longer available, use DESTDIR instead)
|
||||||
|
endif
|
||||||
|
|
||||||
|
INSTALLDIR ?=
|
||||||
|
DESTDIR ?=
|
||||||
|
|
||||||
|
USRSBINDIR = $(INSTALLDIR)/usr/sbin
|
||||||
|
USRBINDIR = $(INSTALLDIR)/usr/bin
|
||||||
|
BINDIR = $(INSTALLDIR)/sbin
|
||||||
|
LIBDIR = $(INSTALLDIR)/lib
|
||||||
|
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||||
|
MANDIR = $(INSTALLDIR)/usr/share/man
|
||||||
|
VARDIR = $(INSTALLDIR)/var
|
||||||
|
TOOLS_DATADIR = $(INSTALLDIR)/usr/share/s390-tools
|
||||||
|
TOOLS_LIBDIR = $(INSTALLDIR)/lib/s390-tools
|
||||||
|
ZFCPDUMP_DIR = $(INSTALLDIR)/lib/s390-tools/zfcpdump
|
||||||
|
# Systemd support files are installed only if a directory is specified
|
||||||
|
# for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system)
|
||||||
|
SYSTEMDSYSTEMUNITDIR =
|
||||||
|
|
||||||
|
INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||||
|
$(SYSCONFDIR) $(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||||
|
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR)
|
||||||
|
OWNER = $(shell id -un)
|
||||||
|
GROUP = $(shell id -gn)
|
||||||
|
export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP
|
||||||
|
|
||||||
|
# Special defines for zfcpdump
|
||||||
|
ZFCPDUMP_PART_IMAGE = zfcpdump_part.image
|
||||||
|
ZFCPDUMP_PART_RD = zfcpdump_part.rd
|
||||||
|
export ZFCPDUMP_DIR ZFCPDUMP_PART_IMAGE ZFCPDUMP_PART_RD
|
||||||
|
|
||||||
|
CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||||
|
HOSTCFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||||
|
CPPFLAGS ?= $(DEFAULT_CPPFLAGS)
|
||||||
|
LDFLAGS ?= $(DEFAULT_LDFLAGS)
|
||||||
|
|
||||||
|
ALL_CFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
|
||||||
|
-DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \
|
||||||
|
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
|
||||||
|
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
|
||||||
|
$(CFLAGS)
|
||||||
|
CXXFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
|
||||||
|
ALL_CXXFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
|
||||||
|
-DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \
|
||||||
|
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
|
||||||
|
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
|
||||||
|
$(CXXFLAGS)
|
||||||
|
ALL_CPPFLAGS = -I $(rootdir)include $(CPPFLAGS)
|
||||||
|
ALL_LDFLAGS = $(LDFLAGS)
|
||||||
|
|
||||||
|
# make G=1
|
||||||
|
# Compile tools so that gcov can be used to collect code coverage data.
|
||||||
|
# See the gcov man page for details.
|
||||||
|
ifeq ("${G}","1")
|
||||||
|
ALL_CFLAGS := $(filter-out -O%,$(ALL_CFLAGS)) --coverage
|
||||||
|
ALL_CXXFLAGS := $(filter-out -O%,$(ALL_CXXFLAGS)) --coverage
|
||||||
|
ALL_LDFLAGS += --coverage
|
||||||
|
endif
|
||||||
|
export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS CXXFLAGS \
|
||||||
|
LDFLAGS CPPFLAGS ALL_CFLAGS ALL_CXXFLAGS ALL_LDFLAGS ALL_CPPFLAGS
|
||||||
|
|
||||||
|
# Overwrite implicite makefile rules for having nice compile output
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@
|
||||||
|
ifeq ("${C}","1")
|
||||||
|
$(CHECKTOOL) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@
|
||||||
|
endif
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
$(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%: %.o
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
%.a:
|
||||||
|
$(AR) rcs $@ $^
|
||||||
|
|
||||||
|
all:
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo 'Usage: make [TARGETS] [OPTIONS]'
|
||||||
|
@echo ''
|
||||||
|
@echo 'TARGETS'
|
||||||
|
@echo ' all Build all tools (default target)'
|
||||||
|
@echo ' install Install tools'
|
||||||
|
@echo ' clean Delete all generated files'
|
||||||
|
@echo ''
|
||||||
|
@echo 'OPTIONS'
|
||||||
|
@echo ' D=1 Build with debugging option "-Og"'
|
||||||
|
@echo ' C=1 Build with check tool defined with "CHECK=" (default=sparse)'
|
||||||
|
@echo ' G=1 Build with gcov to collect code coverage data'
|
||||||
|
@echo ' V=1 Generate verbose build output'
|
||||||
|
@echo ' W=1 Build with higher warning level'
|
||||||
|
@echo ''
|
||||||
|
@echo 'EXAMPLES'
|
||||||
|
@echo ' # make clean all D=1 W=1 -j'
|
||||||
|
@echo ' # make C=1 CHECK=smatch'
|
||||||
|
.PHONY: help
|
||||||
|
|
||||||
|
# Automatic dependency generation
|
||||||
|
#
|
||||||
|
# Create ".o.d" dependency files with the -MM compile option for all ".c" and
|
||||||
|
# ".cpp" files in the directory of the Makefile that includes common.mak:
|
||||||
|
#
|
||||||
|
# $ gcc -MM vmcp.c
|
||||||
|
# vmcp.o: vmcp.c vmcp.h ../include/zt_common.h
|
||||||
|
#
|
||||||
|
# Use -MM instead of -M to *not* mention system header files. We expect
|
||||||
|
# "make clean all" in case of system header updates.
|
||||||
|
|
||||||
|
# We consider header files in three possible directories
|
||||||
|
sources_h = \
|
||||||
|
$(wildcard *.h) \
|
||||||
|
$(wildcard ../include/*.h) \
|
||||||
|
$(wildcard $(rootdir)/include/lib/*.h)
|
||||||
|
|
||||||
|
# Rules to create ".o.d" files out of ".c" or ".cpp" files:
|
||||||
|
|
||||||
|
.%.o.d: %.c $(sources_h)
|
||||||
|
$(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@
|
||||||
|
.%.o.d: %.cpp $(sources_h)
|
||||||
|
$(CXX_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $< > $@
|
||||||
|
# The sources_c/cpp variable contains a list of all ".c" or ".cpp" files in
|
||||||
|
# in the current directory.
|
||||||
|
sources_c = $(wildcard *.c)
|
||||||
|
sources_cpp = $(wildcard *.cpp)
|
||||||
|
# The dependencies_c/cpp variable contains a list of all ".o.d" files,
|
||||||
|
# one for each ".c" or ".cpp" file.
|
||||||
|
dependencies_c = $(sources_c:%.c=.%.o.d)
|
||||||
|
dependencies_cpp = $(sources_cpp:%.cpp=.%.o.d)
|
||||||
|
# Include all ".o.d" dependency files for all make targets except for "clean"
|
||||||
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
|
-include $(dependencies_c)
|
||||||
|
-include $(dependencies_cpp)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Rules for internal libraries needed to ensure that these files are build
|
||||||
|
# with their own build flags even if they are build from external directories.
|
||||||
|
#
|
||||||
|
# Because of the PHONY directory dependency all tools that use libraries
|
||||||
|
# check the library directory via "make -C" when the tools Makefile is
|
||||||
|
# processed.
|
||||||
|
|
||||||
|
$(rootdir)/libutil/libutil.a: $(rootdir)/libutil
|
||||||
|
$(MAKE) -C $(rootdir)/libutil/ libutil.a
|
||||||
|
.PHONY: $(rootdir)/libutil
|
||||||
|
|
||||||
|
$(rootdir)/libccw/libccw.a: $(rootdir)/libccw
|
||||||
|
$(MAKE) -C $(rootdir)/libccw/ libccw.a
|
||||||
|
.PHONY: $(rootdir)/libccw
|
||||||
|
|
||||||
|
$(rootdir)/libvtoc/libvtoc.a: $(rootdir)/libvtoc
|
||||||
|
$(MAKE) -C $(rootdir)/libvtoc/ libvtoc.a
|
||||||
|
.PHONY: $(rootdir)/libvtoc
|
||||||
|
|
||||||
|
$(rootdir)/libdasd/libdasd.a: $(rootdir)/libdasd
|
||||||
|
$(MAKE) -C $(rootdir)/libdasd/ libdasd.a
|
||||||
|
.PHONY: $(rootdir)/libdasd
|
||||||
|
|
||||||
|
$(rootdir)/libzds/libzds.a: $(rootdir)/libzds
|
||||||
|
$(MAKE) -C $(rootdir)/libzds/ libzds.a
|
||||||
|
.PHONY: $(rootdir)/libzds
|
||||||
|
|
||||||
|
$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s
|
||||||
|
$(MAKE) -C $(rootdir)/libu2s/ libu2s.a
|
||||||
|
.PHONY: $(rootdir)/libu2s
|
||||||
|
|
||||||
|
$(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump
|
||||||
|
$(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a
|
||||||
|
.PHONY: $(rootdir)/libvmdump
|
||||||
|
|
||||||
|
$(rootdir)/zipl/boot/data.o:
|
||||||
|
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
||||||
|
|
||||||
|
install_dirs:
|
||||||
|
for dir in $(INSTDIRS); do \
|
||||||
|
test -d $(DESTDIR)$$dir || $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$$dir; \
|
||||||
|
done
|
||||||
|
for i in 1 2 3 4 5 6 7 8; do \
|
||||||
|
test -d $(DESTDIR)$(MANDIR)/man$$i || $(INSTALL) -g $(GROUP) -o $(OWNER) \
|
||||||
|
-d $(DESTDIR)$(MANDIR)/man$$i; \
|
||||||
|
done
|
||||||
|
|
||||||
|
install_echo:
|
||||||
|
$(call echocmd," INSTALL ")
|
||||||
|
|
||||||
|
install: install_echo install_dirs
|
||||||
|
|
||||||
|
clean_echo:
|
||||||
|
$(call echocmd," CLEAN ")
|
||||||
|
clean_gcov:
|
||||||
|
rm -f *.gcda *.gcno *.gcov
|
||||||
|
clean_dep:
|
||||||
|
rm -f .*.o.d
|
||||||
|
|
||||||
|
clean: clean_echo clean_gcov clean_dep
|
||||||
|
endif
|
||||||
|
|
||||||
41
cpacfstats/Makefile
Normal file
41
cpacfstats/Makefile
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
ifeq (${HAVE_PFM},0)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_PFM=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_PFM=0
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
check_dep:
|
||||||
|
$(call check_dep, \
|
||||||
|
"cpacfstats", \
|
||||||
|
"perfmon/pfmlib.h", \
|
||||||
|
"libpfm-devel or libpfm4-dev", \
|
||||||
|
"HAVE_PFM=0")
|
||||||
|
|
||||||
|
all: check_dep cpacfstats cpacfstatsd
|
||||||
|
|
||||||
|
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -lpfm -o $@
|
||||||
|
|
||||||
|
cpacfstats: cpacfstats.o stats_sock.o
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -m 755 cpacfstatsd $(DESTDIR)$(USRSBINDIR)
|
||||||
|
$(INSTALL) -m 755 cpacfstats $(DESTDIR)$(USRBINDIR)
|
||||||
|
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
|
endif
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ cpacfstatsd cpacfstats
|
||||||
|
|
||||||
|
.PHONY: all clean install check_dep
|
||||||
127
cpacfstats/cpacfstats.1
Normal file
127
cpacfstats/cpacfstats.1
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
.\" cpacfstats.1
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2015, 2017
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.\" use
|
||||||
|
.\" groff -man -Tutf8 cpacfstats.1
|
||||||
|
.\" or
|
||||||
|
.\" nroff -man cpacfstats.1
|
||||||
|
.\" to process this source
|
||||||
|
.\"
|
||||||
|
.TH cpacfstats "1" "January 2015" "s390-tools"
|
||||||
|
.
|
||||||
|
.ds c \fcpacfstats\fP
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
cpacfstats \- enable, disable and display CPACF statistical data
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cpacfstats
|
||||||
|
.RB [ \-h | \-\-help ]
|
||||||
|
.RB [ \-v | \-\-version ]
|
||||||
|
.RB [ \-e | \-\-enable
|
||||||
|
.I counter
|
||||||
|
.RB ]
|
||||||
|
.RB [ \-d | \-\-disable
|
||||||
|
.I counter
|
||||||
|
.RB ]
|
||||||
|
.RB [ \-r | \-\-reset
|
||||||
|
.I counter
|
||||||
|
.RB ]
|
||||||
|
.RB [ \-p | \-\-print
|
||||||
|
.I counter
|
||||||
|
.RB ]
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The cpacfstats client application interacts with the cpacfstatsd daemon and
|
||||||
|
triggers actions. The application enables, disables, resets, and fetches
|
||||||
|
one or all of the mainframe CPACF performance counters with the help of the
|
||||||
|
daemon process.
|
||||||
|
|
||||||
|
All counters are initially disabled and must be switched on to measure
|
||||||
|
CPACF activities of the system. There is a slight performance penalty with
|
||||||
|
CPACF counters enabled.
|
||||||
|
|
||||||
|
Note: All cryptographic counters are bound to physical CPUs. If you use
|
||||||
|
dedicated CPUs for your LPAR, you obtain only your CPACF activities. If
|
||||||
|
your CPUs are shared with other LPARs, their cryptographic activities might
|
||||||
|
distort the results. CPACF performance counters are available on LPARs
|
||||||
|
only. For security reasons only members of the group \fIcpacfstats\fR are
|
||||||
|
allowed to run the cpacfstats client application.
|
||||||
|
|
||||||
|
Example usage scenario:
|
||||||
|
.P
|
||||||
|
1. Start the cpacfstatsd daemon with root privileges.
|
||||||
|
.P
|
||||||
|
2. Check for successful startup by using the ps and syslog commands.
|
||||||
|
.P
|
||||||
|
3. Enable the CPACF counters of interest. For example, enable all counters
|
||||||
|
by issuing cpacfstats -e.
|
||||||
|
.P
|
||||||
|
4. Run your applications.
|
||||||
|
.P
|
||||||
|
5. Display counter values by using the cpacfstats command. Reset the
|
||||||
|
cryptographic counters as required. To reset, use, for example, cpacfstats
|
||||||
|
-r.
|
||||||
|
.P
|
||||||
|
6. Disable all the CPACF measurements, for example, by using cpacfstats
|
||||||
|
-d.
|
||||||
|
.P
|
||||||
|
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR or \fB\-\-help\fR
|
||||||
|
Display help information for the command.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR or \fB\-\-version\fR
|
||||||
|
Display version and copyright information for the command.
|
||||||
|
.TP
|
||||||
|
\fB\-e\fR or \fB\-\-enable\fR [counter]
|
||||||
|
Enable one or all CPACF performance counters. The optional counter argument
|
||||||
|
can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or \fBall\fR. If
|
||||||
|
the counter argument is omitted, all performance counters are
|
||||||
|
enabled. Enabling a counter does not reset it. New events are added to the
|
||||||
|
current counter value.
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR or \fB\-\-disable\fR [counter]
|
||||||
|
Disable one or all CPACF performance counters. The optional counter
|
||||||
|
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
|
||||||
|
\fBall\fR. If the counter argument is omitted, all performance counters
|
||||||
|
are disabled. Disabling a counter does not reset it. The counter value is
|
||||||
|
preserved when a counter is disabled, and counting will resume using the
|
||||||
|
preserved value when the counter is re-enabled.
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR or \fB\-\-reset\fR [counter]
|
||||||
|
Reset one or all CPACF performance counters. The optional counter
|
||||||
|
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
|
||||||
|
\fBall\fR. If the counter argument is omitted, all performance counters are
|
||||||
|
reset to 0.
|
||||||
|
.TP
|
||||||
|
\fB\-p\fR or \fB\-\-print\fR [counter]
|
||||||
|
Display the value of one or all CPACF performance counters. The optional
|
||||||
|
counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR
|
||||||
|
or \fBall\fR. If the counter argument is omitted or if there is no
|
||||||
|
argument, all performance counters are displayed.
|
||||||
|
.TP
|
||||||
|
The default command is --print all.
|
||||||
|
.
|
||||||
|
.SH FILES
|
||||||
|
.nf
|
||||||
|
/var/run/cpacfstatsd_socket
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.SH RETURN VALUE
|
||||||
|
.IP 0
|
||||||
|
Successful program execution.
|
||||||
|
.IP 1
|
||||||
|
An error occurred, reasons include: invalid argument, cpacfstatsd could not
|
||||||
|
be reached (check that the daemon is running), insufficient access rights,
|
||||||
|
version mismatch between client and daemon, or the application is out of
|
||||||
|
memory. The application prints a message with the details of the error and
|
||||||
|
the errno value.
|
||||||
|
.
|
||||||
|
.SH SEE ALSO
|
||||||
|
cpacfstatsd (8)
|
||||||
237
cpacfstats/cpacfstats.c
Normal file
237
cpacfstats/cpacfstats.c
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* cpacfstats - display and maintain CPACF perf counters
|
||||||
|
*
|
||||||
|
* cpacfstats client implementation
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2015, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
#include "cpacfstats.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char *const name = "cpacfstats";
|
||||||
|
|
||||||
|
static const char *const usage =
|
||||||
|
"Usage: %s [OPTIONS [COUNTER]]\n"
|
||||||
|
"\n"
|
||||||
|
"Enable, disable, reset and read CPACF Crypto Activity Counters\n"
|
||||||
|
"Use OPTIONS described below:\n"
|
||||||
|
"\n"
|
||||||
|
"\t-h, --help Print this help, then exit\n"
|
||||||
|
"\t-v, --version Print version information, then exit\n"
|
||||||
|
"\t-e, --enable [counter] Enable one or all counters\n"
|
||||||
|
"\t-d, --disable [counter] Disable one or all counters\n"
|
||||||
|
"\t-r, --reset [counter] Reset one or all counter values\n"
|
||||||
|
"\t-p, --print [counter] Print one or all counter values\n"
|
||||||
|
"\tcounter can be: 'aes' 'des' 'rng' 'sha' or 'all'\n";
|
||||||
|
|
||||||
|
static const char *const counter_str[] = {
|
||||||
|
[DES_FUNCTIONS] = "des",
|
||||||
|
[AES_FUNCTIONS] = "aes",
|
||||||
|
[SHA_FUNCTIONS] = "sha",
|
||||||
|
[PRNG_FUNCTIONS] = "rng",
|
||||||
|
[ALL_COUNTER] = "all"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
struct msg m;
|
||||||
|
|
||||||
|
memset(&m, 0, sizeof(m));
|
||||||
|
|
||||||
|
m.head.m_ver = VERSION;
|
||||||
|
m.head.m_type = QUERY;
|
||||||
|
m.query.m_ctr = ctr;
|
||||||
|
m.query.m_cmd = cmd;
|
||||||
|
|
||||||
|
return send_msg(s, &m);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int recv_answer(int s, int *ctr, int *state, uint64_t *value)
|
||||||
|
{
|
||||||
|
struct msg m;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = recv_msg(s, &m);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (m.head.m_ver != VERSION) {
|
||||||
|
eprint("Received msg with wrong version %d != %d\n",
|
||||||
|
m.head.m_ver, VERSION);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (m.head.m_type != ANSWER) {
|
||||||
|
eprint("Received msg with wrong type %d != %d\n",
|
||||||
|
m.head.m_type, ANSWER);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*ctr = m.answer.m_ctr;
|
||||||
|
*state = m.answer.m_state;
|
||||||
|
*value = m.answer.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_answer(int ctr, int state, uint64_t value)
|
||||||
|
{
|
||||||
|
if (state < 0)
|
||||||
|
printf(" %s counter: error state %d\n",
|
||||||
|
counter_str[ctr], state);
|
||||||
|
else if (state == DISABLED)
|
||||||
|
printf(" %s counter: disabled\n", counter_str[ctr]);
|
||||||
|
else
|
||||||
|
printf(" %s counter: %"PRIu64"\n", counter_str[ctr], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int eprint(const char *format, ...)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
va_list vargs;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
i = snprintf(buf, sizeof(buf), "%s: ", name);
|
||||||
|
va_start(vargs, format);
|
||||||
|
n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
|
||||||
|
if (n > 0)
|
||||||
|
fputs(buf, stderr);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
enum ctr_e ctr = ALL_COUNTER;
|
||||||
|
enum cmd_e cmd = PRINT;
|
||||||
|
int i, j, s, state;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
int opt, idx = 0;
|
||||||
|
const struct option long_opts[] = {
|
||||||
|
{ "help", 0, NULL, 'h' },
|
||||||
|
{ "version", 0, NULL, 'v' },
|
||||||
|
{ "enable", 0, NULL, 'e' },
|
||||||
|
{ "disable", 0, NULL, 'd' },
|
||||||
|
{ "reset", 0, NULL, 'r' },
|
||||||
|
{ "print", 0, NULL, 'p' },
|
||||||
|
{ NULL, 0, NULL, 0 } };
|
||||||
|
while (1) {
|
||||||
|
opt = getopt_long(argc, argv,
|
||||||
|
"hvedrp", long_opts, &idx);
|
||||||
|
if (opt == -1)
|
||||||
|
break; /* no more arguments */
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
printf(usage, name);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n"
|
||||||
|
"Version %s\n%s\n",
|
||||||
|
name, RELEASE_STRING, COPYRIGHT);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
cmd = ENABLE;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
cmd = DISABLE;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
cmd = RESET;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
cmd = PRINT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
eprint("Invalid argument, try -h or --help for more information\n");
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* there may be an optional counter argument */
|
||||||
|
if (optind > 0 && optind < argc) {
|
||||||
|
for (i = 0; i <= ALL_COUNTER; i++)
|
||||||
|
if (strcmp(argv[optind], counter_str[i]) == 0)
|
||||||
|
break;
|
||||||
|
if (i > ALL_COUNTER) {
|
||||||
|
eprint("Unknown counter '%s'\n", argv[optind]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ctr = (enum ctr_e) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to open and connect socket to the cpacfstatsd daemon */
|
||||||
|
s = open_socket(CLIENT);
|
||||||
|
if (s < 0) {
|
||||||
|
eprint("Can't connect to daemon\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send query */
|
||||||
|
if (send_query(s, cmd, ctr) != 0) {
|
||||||
|
eprint("Error on sending query message to daemon\n");
|
||||||
|
close(s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctr == ALL_COUNTER) {
|
||||||
|
for (i = 0; i < ALL_COUNTER; i++) {
|
||||||
|
/* receive answer */
|
||||||
|
if (recv_answer(s, &j, &state, &value) != 0) {
|
||||||
|
eprint("Error on receiving answer message from daemon\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (state < 0) {
|
||||||
|
eprint("Received bad status code %d from daemon\n",
|
||||||
|
state);
|
||||||
|
close(s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
print_answer(j, state, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* receive answer */
|
||||||
|
if (recv_answer(s, &j, &state, &value) != 0) {
|
||||||
|
eprint("Error on receiving answer message from daemon\n");
|
||||||
|
close(s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (state < 0) {
|
||||||
|
eprint("Received bad status code %d from daemon\n",
|
||||||
|
state);
|
||||||
|
close(s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
print_answer(j, state, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close connection */
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
112
cpacfstats/cpacfstats.h
Normal file
112
cpacfstats/cpacfstats.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* cpacfstats - display and maintain CPACF perf counters
|
||||||
|
*
|
||||||
|
* common function prototypes and definitions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2015, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPACFSTATS_H
|
||||||
|
#define CPACFSTATS_H
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#define COPYRIGHT "Copyright IBM Corp. 2015, 2017"
|
||||||
|
|
||||||
|
int eprint(const char *format, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Counter names
|
||||||
|
* ALL_COUNTER must always be the last member of the enum
|
||||||
|
*/
|
||||||
|
enum ctr_e {
|
||||||
|
DES_FUNCTIONS = 0,
|
||||||
|
AES_FUNCTIONS,
|
||||||
|
SHA_FUNCTIONS,
|
||||||
|
PRNG_FUNCTIONS,
|
||||||
|
ALL_COUNTER
|
||||||
|
};
|
||||||
|
|
||||||
|
enum type_e {
|
||||||
|
QUERY = 0,
|
||||||
|
ANSWER
|
||||||
|
};
|
||||||
|
|
||||||
|
enum cmd_e {
|
||||||
|
PRINT = 0,
|
||||||
|
ENABLE,
|
||||||
|
DISABLE,
|
||||||
|
RESET
|
||||||
|
};
|
||||||
|
|
||||||
|
enum state_e {
|
||||||
|
DISABLED = 0,
|
||||||
|
ENABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* query send from clent to daemon
|
||||||
|
* Consist of:
|
||||||
|
* enum counter
|
||||||
|
* enum command
|
||||||
|
*/
|
||||||
|
struct msg_query {
|
||||||
|
uint32_t m_ctr;
|
||||||
|
uint32_t m_cmd;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* answer send from daemon to client
|
||||||
|
* Consist of:
|
||||||
|
* enum counter
|
||||||
|
* status code: < 0 error, 0 disabled, > 0 enabled
|
||||||
|
* counter value
|
||||||
|
*/
|
||||||
|
struct msg_answer {
|
||||||
|
uint32_t m_ctr;
|
||||||
|
int32_t m_state;
|
||||||
|
uint64_t m_value;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* stats_sock.c */
|
||||||
|
|
||||||
|
#define SERVER 1
|
||||||
|
#define CLIENT 2
|
||||||
|
|
||||||
|
#define BACKLOG 10
|
||||||
|
|
||||||
|
#define SOCKET_FILE "/var/run/cpacfstatsd_socket"
|
||||||
|
#define PID_FILE "/var/run/cpacfstatsd.pid"
|
||||||
|
|
||||||
|
#define CPACFSTATS_GROUP "cpacfstats"
|
||||||
|
|
||||||
|
struct msg_header {
|
||||||
|
uint32_t m_ver;
|
||||||
|
uint32_t m_type;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct msg {
|
||||||
|
struct msg_header head;
|
||||||
|
union {
|
||||||
|
struct msg_query query;
|
||||||
|
struct msg_answer answer;
|
||||||
|
};
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
int open_socket(int mode);
|
||||||
|
int send_msg(int sfd, struct msg *m);
|
||||||
|
int recv_msg(int sfd, struct msg *m);
|
||||||
|
|
||||||
|
/* perf_crypto.c */
|
||||||
|
|
||||||
|
int perf_init(void);
|
||||||
|
void perf_close(void);
|
||||||
|
int perf_enable_ctr(enum ctr_e ctr);
|
||||||
|
int perf_disable_ctr(enum ctr_e ctr);
|
||||||
|
int perf_reset_ctr(enum ctr_e ctr);
|
||||||
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
|
||||||
|
|
||||||
|
#endif
|
||||||
97
cpacfstats/cpacfstatsd.8
Normal file
97
cpacfstats/cpacfstatsd.8
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.\" cpacfstatsd.8
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2015, 2017
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.\" use
|
||||||
|
.\" groff -man -Tutf8 cpacfstatsd.8
|
||||||
|
.\" or
|
||||||
|
.\" nroff -man cpacfstatsd.8
|
||||||
|
.\" to process this source
|
||||||
|
.\"
|
||||||
|
.TH cpacfstatsd "8" "January 2015" "s390-tools"
|
||||||
|
.
|
||||||
|
.ds c \fcpacfstatsd\fP
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
cpacfstatsd \- CPACF statistics collection daemon process
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cpacfstatsd
|
||||||
|
.RB [ \-h | \-\-help ]
|
||||||
|
.RB [ \-v | \-\-version ]
|
||||||
|
.RB [ \-f | \-\-foreground ]
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The cpacfstatsd controlling daemon enables, disables, resets, and fetches
|
||||||
|
the mainframe CPACF performance counter registers.
|
||||||
|
The daemon receives commands from the user application cpacfstats through
|
||||||
|
the UNIX Domain Socket, processes them and returns the requested
|
||||||
|
information. For all available commands, see the cpacfstats man page.
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
.P
|
||||||
|
- The running Linux kernel must have the the CONFIG_PERF_EVENTS
|
||||||
|
config option enabled.
|
||||||
|
.P
|
||||||
|
- Libpfm version 4 or higher is needed to successfully run the daemon.
|
||||||
|
.P
|
||||||
|
- Your LPAR must be configured to enable the "Counter Facility Security Options".
|
||||||
|
.P
|
||||||
|
- The daemon requires root privileges to interact with the performance
|
||||||
|
ioctls of the kernel.
|
||||||
|
|
||||||
|
CPU hotplug is not recognized by the daemon. When adding or removing a CPU,
|
||||||
|
restart the daemon to ensure correct summing of the per-CPU performance
|
||||||
|
counters.
|
||||||
|
|
||||||
|
The starting daemon first checks for any stale pid file
|
||||||
|
/var/run/cpacfstatsd.pid. If this file exists, and the process ID in the
|
||||||
|
file belongs to an active process, an error message is printed to the
|
||||||
|
console and the program terminates.
|
||||||
|
|
||||||
|
The daemon and the client cpacfstats communicate through a Unix Domain
|
||||||
|
Socket. This socket is created by the daemon at startup with the associated
|
||||||
|
socket file /run/cpacfstatsd_socket. For security reasons only members of
|
||||||
|
the group \fIcpacfstats\fR are allowed to communicate with the daemon. A
|
||||||
|
system administrator should create this group and add all users which are
|
||||||
|
allowed to run the cpacfstats client to the group.
|
||||||
|
|
||||||
|
After startup, the daemon runs in the background and detaches from any
|
||||||
|
terminal. Errors and warnings are posted to the syslog subsystem. Check the
|
||||||
|
process list and the system syslog messages for confirmation of successful
|
||||||
|
startup.
|
||||||
|
|
||||||
|
On regular termination the pid file, the communication socket and the
|
||||||
|
associated file is removed gracefully.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR or \fB\-\-help\fR
|
||||||
|
Display help information for the command.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR or \fB\-\-version\fR
|
||||||
|
Display version and copyright information for the command.
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR or \fB\-\-foreground\fR
|
||||||
|
Run the daemon in foreground mode, thus printing errors to stderr instead
|
||||||
|
of posting them through syslog. This option might be useful when debugging
|
||||||
|
daemon startup and initialization failures.
|
||||||
|
|
||||||
|
.SH FILES
|
||||||
|
.nf
|
||||||
|
/var/run/cpacfstatsd_socket
|
||||||
|
/var/run/cpacfstatsd.pid
|
||||||
|
.fi
|
||||||
|
|
||||||
|
.SH RETURN VALUE
|
||||||
|
.IP 0
|
||||||
|
The daemon was successfully set to run in the background. This does not
|
||||||
|
imply that the daemon startup was successful, as the main initialization is
|
||||||
|
done in the re-spawned process. Check the syslog for success or failure.
|
||||||
|
.IP 1
|
||||||
|
The daemon could not be set to run in the background.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
cpacfstats (1)
|
||||||
477
cpacfstats/cpacfstatsd.c
Normal file
477
cpacfstats/cpacfstatsd.c
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
/*
|
||||||
|
* cpacfstats - display and maintain CPACF perf counters
|
||||||
|
*
|
||||||
|
* cpacfstatsd daemon implementation
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2015, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
#include "cpacfstats.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char *const name = "cpacfstatsd";
|
||||||
|
|
||||||
|
static const char *const usage =
|
||||||
|
"Usage: %s [OPTIONS]\n"
|
||||||
|
"\n"
|
||||||
|
"Daemon to provide access to CPACF perf counters\n"
|
||||||
|
"Use OPTIONS described below:\n"
|
||||||
|
"\n"
|
||||||
|
"\t-h, --help Print this help, then exit\n"
|
||||||
|
"\t-v, --version Print version information, then exit\n"
|
||||||
|
"\t-f, --foreground Run in foreground, do not detach\n";
|
||||||
|
|
||||||
|
static int daemonized;
|
||||||
|
|
||||||
|
static int ctr_state[ALL_COUNTER];
|
||||||
|
|
||||||
|
|
||||||
|
static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
|
||||||
|
{
|
||||||
|
struct msg m;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = recv_msg(s, &m);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (m.head.m_ver != VERSION) {
|
||||||
|
eprint("Received msg with wrong version %d != %d\n",
|
||||||
|
m.head.m_ver, VERSION);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (m.head.m_type != QUERY) {
|
||||||
|
eprint("Received msg with wrong type %d != %d\n",
|
||||||
|
m.head.m_type, QUERY);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*ctr = m.query.m_ctr;
|
||||||
|
*cmd = m.query.m_cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int send_answer(int s, int ctr, int state, uint64_t value)
|
||||||
|
{
|
||||||
|
struct msg m;
|
||||||
|
|
||||||
|
memset(&m, 0, sizeof(m));
|
||||||
|
|
||||||
|
m.head.m_ver = VERSION;
|
||||||
|
m.head.m_type = ANSWER;
|
||||||
|
m.answer.m_ctr = ctr;
|
||||||
|
m.answer.m_state = state;
|
||||||
|
m.answer.m_value = value;
|
||||||
|
|
||||||
|
return send_msg(s, &m);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int do_enable(int s, enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
uint64_t value;
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; i++) {
|
||||||
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
|
if (!ctr_state[i]) {
|
||||||
|
rc = perf_enable_ctr(i);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctr_state[i] = 1;
|
||||||
|
}
|
||||||
|
rc = perf_read_ctr(i, &value);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_answer(s, i, ENABLED, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int do_disable(int s, enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; i++) {
|
||||||
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
|
if (ctr_state[i]) {
|
||||||
|
rc = perf_disable_ctr(i);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctr_state[i] = 0;
|
||||||
|
}
|
||||||
|
send_answer(s, i, DISABLED, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int do_reset(int s, enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; i++) {
|
||||||
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
|
if (ctr_state[i]) {
|
||||||
|
rc = perf_reset_ctr(i);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_answer(s, i, ENABLED, 0);
|
||||||
|
} else {
|
||||||
|
send_answer(s, i, DISABLED, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_print(int s, enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int i, rc = 0;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; i++) {
|
||||||
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
|
if (ctr_state[i]) {
|
||||||
|
rc = perf_read_ctr(i, &value);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_answer(s, i, ENABLED, value);
|
||||||
|
} else {
|
||||||
|
send_answer(s, i, DISABLED, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int become_daemon(void)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* syslog */
|
||||||
|
openlog("cpacfstatsd", 0, LOG_DAEMON);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fork and terminate parent
|
||||||
|
* Reasons:
|
||||||
|
* - opens new command line prompt
|
||||||
|
* - the child process is guaranteed not to be the process group leader
|
||||||
|
* nessecarry for setsid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (fork()) {
|
||||||
|
case -1: /* error */
|
||||||
|
eprint("Fork() failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
case 0: /* child */
|
||||||
|
break;
|
||||||
|
default: /* parent */
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir("/") != 0) {
|
||||||
|
eprint("Chdir('/') failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start new session */
|
||||||
|
if (setsid() == -1) {
|
||||||
|
eprint("Setsid() failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear umask so that socket has right default permission */
|
||||||
|
umask(0007);
|
||||||
|
|
||||||
|
/* make stdin, stdout and stderr use /dev/null */
|
||||||
|
fd = open("/dev/null", O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Could not open /dev/null, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
daemonized = 1;
|
||||||
|
|
||||||
|
/* make pid file, fails if the file exists */
|
||||||
|
f = fopen(PID_FILE, "w+x");
|
||||||
|
if (!f) {
|
||||||
|
eprint("Couldn't create pid file '%s', errno=%d [%s]\n",
|
||||||
|
PID_FILE, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fprintf(f, "%lu", (unsigned long)getpid());
|
||||||
|
fflush(f);
|
||||||
|
fclose(f);
|
||||||
|
chmod(PID_FILE, 0644);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void remove_sock(void)
|
||||||
|
{
|
||||||
|
remove(SOCKET_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_pidfile(void)
|
||||||
|
{
|
||||||
|
unsigned long pid;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen(PID_FILE, "r");
|
||||||
|
if (!f) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
/* pid file does not exit, pid file check is ok */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* unknown errno, pid file check is not ok */
|
||||||
|
eprint("Unknown error on pid file check '%s', errno=%d [%s]\n",
|
||||||
|
PID_FILE, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pid file could be opened, scan pid in there */
|
||||||
|
if (fscanf(f, "%lu", &pid) != 1) {
|
||||||
|
/*
|
||||||
|
* invalid, maybe a leftover from a previous run
|
||||||
|
* remove and return pid file check ok
|
||||||
|
*/
|
||||||
|
fclose(f);
|
||||||
|
remove(PID_FILE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
/* check if this process is still running */
|
||||||
|
if (kill(pid, 0) != 0) {
|
||||||
|
/*
|
||||||
|
* failure, assume this means there is no such pid running
|
||||||
|
* remove pid file and return pid file check ok
|
||||||
|
*/
|
||||||
|
remove(PID_FILE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* looks like there is another cpacfstatsd running
|
||||||
|
* return with pid file check failure
|
||||||
|
*/
|
||||||
|
eprint("Looks like there is another cpacfstatsd (pid=%lu) running\n",
|
||||||
|
pid);
|
||||||
|
eprint("Please check and maybe remove stale pid file '%s'\n",
|
||||||
|
PID_FILE);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_pidfile(void)
|
||||||
|
{
|
||||||
|
remove(PID_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void signalhandler(int sig)
|
||||||
|
{
|
||||||
|
if (sig == SIGTERM)
|
||||||
|
eprint("Caught signal SIGTERM, terminating...\n");
|
||||||
|
else if (sig == SIGINT)
|
||||||
|
eprint("Caught signal SIGINT, terminating...\n");
|
||||||
|
else
|
||||||
|
eprint("Caught signal %d, terminating...\n", sig);
|
||||||
|
|
||||||
|
remove_sock();
|
||||||
|
perf_close();
|
||||||
|
remove_pidfile();
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int eprint(const char *format, ...)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
va_list vargs;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
i = snprintf(buf, sizeof(buf), "%s: ", name);
|
||||||
|
va_start(vargs, format);
|
||||||
|
n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
if (daemonized)
|
||||||
|
syslog(LOG_WARNING, "%s", buf);
|
||||||
|
else
|
||||||
|
fputs(buf, stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int rc, sfd, foreground = 0;
|
||||||
|
struct sigaction act;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
int opt, idx = 0;
|
||||||
|
const struct option long_opts[] = {
|
||||||
|
{ "help", 0, NULL, 'h' },
|
||||||
|
{ "foreground", 0, NULL, 'f' },
|
||||||
|
{ "version", 0, NULL, 'v' },
|
||||||
|
{ NULL, 0, NULL, 0 } };
|
||||||
|
while (1) {
|
||||||
|
opt = getopt_long(argc, argv,
|
||||||
|
"hfv", long_opts, &idx);
|
||||||
|
if (opt == -1)
|
||||||
|
break; /* no more arguments */
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
printf(usage, name);
|
||||||
|
exit(0);
|
||||||
|
case 'f':
|
||||||
|
foreground = 1;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n"
|
||||||
|
"Version %s\n%s\n",
|
||||||
|
name, RELEASE_STRING, COPYRIGHT);
|
||||||
|
exit(0);
|
||||||
|
default:
|
||||||
|
printf("%s: Invalid argument, try -h or --help for more information\n",
|
||||||
|
name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_pidfile() != 0) {
|
||||||
|
eprint("Stalled pid file or daemon allready running, terminating\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foreground) {
|
||||||
|
if (become_daemon() != 0) {
|
||||||
|
eprint("Couldn't daemonize\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perf_init() != 0) {
|
||||||
|
eprint("Couldn't initialize perf lib\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
atexit(perf_close);
|
||||||
|
|
||||||
|
sfd = open_socket(SERVER);
|
||||||
|
if (sfd < 0) {
|
||||||
|
eprint("Couldn't initialize server socket\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
atexit(remove_sock);
|
||||||
|
|
||||||
|
memset(&act, 0, sizeof(act));
|
||||||
|
act.sa_handler = signalhandler;
|
||||||
|
act.sa_flags = 0;
|
||||||
|
if (sigaction(SIGINT, &act, 0) != 0) {
|
||||||
|
eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (sigaction(SIGTERM, &act, 0) != 0) {
|
||||||
|
eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
eprint("Running\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
enum ctr_e ctr;
|
||||||
|
enum cmd_e cmd;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = accept(sfd, NULL, NULL);
|
||||||
|
if (s < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
eprint("Accept() failure, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = recv_query(s, &ctr, &cmd);
|
||||||
|
if (rc != 0) {
|
||||||
|
eprint("Recv_query() failed, ignoring\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == ENABLE)
|
||||||
|
rc = do_enable(s, ctr);
|
||||||
|
else if (cmd == DISABLE)
|
||||||
|
rc = do_disable(s, ctr);
|
||||||
|
else if (cmd == RESET)
|
||||||
|
rc = do_reset(s, ctr);
|
||||||
|
else if (cmd == PRINT)
|
||||||
|
rc = do_print(s, ctr);
|
||||||
|
else {
|
||||||
|
eprint("Received unknown command %d, ignoring\n",
|
||||||
|
(int) cmd);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
close(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
258
cpacfstats/perf_crypto.c
Normal file
258
cpacfstats/perf_crypto.c
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* cpacfstats - display and maintain CPACF perf counters
|
||||||
|
*
|
||||||
|
* low level perf functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2015, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <perfmon/perf_event.h>
|
||||||
|
#include <perfmon/pfmlib.h>
|
||||||
|
#include <perfmon/pfmlib_perf_event.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "cpacfstats.h"
|
||||||
|
|
||||||
|
/* correlation between counter and perf counter string */
|
||||||
|
static const struct {
|
||||||
|
char pfm_name[80];
|
||||||
|
enum ctr_e ctr;
|
||||||
|
} pmf_counter_name[ALL_COUNTER] = {
|
||||||
|
{"cpum_cf::DEA_FUNCTIONS", DES_FUNCTIONS},
|
||||||
|
{"cpum_cf::AES_FUNCTIONS", AES_FUNCTIONS},
|
||||||
|
{"cpum_cf::SHA_FUNCTIONS", SHA_FUNCTIONS},
|
||||||
|
{"cpum_cf::PRNG_FUNCTIONS", PRNG_FUNCTIONS}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need one filedescriptor per CPU per counter.
|
||||||
|
* So perf_init builds this:
|
||||||
|
*
|
||||||
|
* ctr_fds - is an array of pointers to file descriptor arrays.
|
||||||
|
* Each file descriptor array has space for number of logical CPUs + 1
|
||||||
|
* filedescriptors (int values). The last element of each file descriptor
|
||||||
|
* array is always 0, assuming there will never appear a filedescriptor
|
||||||
|
* with value 0:
|
||||||
|
*
|
||||||
|
* ctr_fds:
|
||||||
|
* ctr_fds[0] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
||||||
|
* ctr_fds[1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
||||||
|
* ...
|
||||||
|
* ctr_fds[ALL_COUNTER-1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
||||||
|
*/
|
||||||
|
static int *ctr_fds[ALL_COUNTER];
|
||||||
|
|
||||||
|
|
||||||
|
int perf_init(void)
|
||||||
|
{
|
||||||
|
int i, cpus, ctr, cpu, ec, *fds;
|
||||||
|
|
||||||
|
memset(ctr_fds, 0, sizeof(ctr_fds));
|
||||||
|
|
||||||
|
/* initialize performance monitoring library */
|
||||||
|
ec = pfm_initialize();
|
||||||
|
if (ec != PFM_SUCCESS) {
|
||||||
|
eprint("Pfm_initialize() returned with failure (%d:%s)\n",
|
||||||
|
ec, pfm_strerror(ec));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get number of logical processors */
|
||||||
|
cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
|
||||||
|
/* for each counter */
|
||||||
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate an array of ints to store for each CPU
|
||||||
|
* one filedescriptor + a terminating 0
|
||||||
|
*/
|
||||||
|
fds = (int *) calloc(sizeof(int), cpus+1);
|
||||||
|
if (!fds) {
|
||||||
|
eprint("Malloc() of %d byte failed, errno=%d [%s]\n",
|
||||||
|
(int)(sizeof(int) * (cpus+1)),
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr_fds[ctr] = fds;
|
||||||
|
|
||||||
|
for (cpu = 0; cpu < cpus; cpu++) {
|
||||||
|
pfm_perf_encode_arg_t pfm_arg;
|
||||||
|
struct perf_event_attr pfm_event;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
memset(&pfm_arg, 0, sizeof(pfm_arg));
|
||||||
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||||
|
pfm_arg.attr = &pfm_event;
|
||||||
|
pfm_arg.size = sizeof(pfm_arg);
|
||||||
|
|
||||||
|
/* search for the counter's corresponding pfm name */
|
||||||
|
for (i = ALL_COUNTER-1; i >= 0; i--)
|
||||||
|
if ((int) pmf_counter_name[i].ctr == ctr)
|
||||||
|
break;
|
||||||
|
if (i < 0) {
|
||||||
|
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
|
||||||
|
ctr, __FILE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode the counters perf event into pfm_arg.attr */
|
||||||
|
ec = pfm_get_os_event_encoding(
|
||||||
|
pmf_counter_name[i].pfm_name,
|
||||||
|
PFM_PLM0,
|
||||||
|
PFM_OS_PERF_EVENT,
|
||||||
|
&pfm_arg);
|
||||||
|
if (ec != PFM_SUCCESS) {
|
||||||
|
eprint("Pfm_initialize() for %s failed (%d:%s)\n",
|
||||||
|
pmf_counter_name[i].pfm_name,
|
||||||
|
ec, pfm_strerror(ec));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fetch file descriptor for this perf event
|
||||||
|
* the counter event should start disabled
|
||||||
|
*/
|
||||||
|
pfm_event.disabled = 1;
|
||||||
|
fd = perf_event_open(
|
||||||
|
&pfm_event,
|
||||||
|
-1, /* pid -1 means all processes */
|
||||||
|
cpu,
|
||||||
|
-1, /* group filedescriptor */
|
||||||
|
0); /* flags */
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fds[cpu] = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void perf_close(void)
|
||||||
|
{
|
||||||
|
int ctr, *fds;
|
||||||
|
|
||||||
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
||||||
|
close(*fds);
|
||||||
|
*fds = 0;
|
||||||
|
}
|
||||||
|
free(ctr_fds[ctr]);
|
||||||
|
ctr_fds[ctr] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_enable_ctr(enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int *fds, ec, rc = 0;
|
||||||
|
|
||||||
|
if (ctr == ALL_COUNTER) {
|
||||||
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
|
rc = perf_enable_ctr(ctr);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
||||||
|
ec = ioctl(*fds, PERF_EVENT_IOC_ENABLE, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_disable_ctr(enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int *fds, ec, rc = 0;
|
||||||
|
|
||||||
|
if (ctr == ALL_COUNTER) {
|
||||||
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
|
rc = perf_disable_ctr(ctr);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
||||||
|
ec = ioctl(*fds, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_reset_ctr(enum ctr_e ctr)
|
||||||
|
{
|
||||||
|
int *fds, ec, rc = 0;
|
||||||
|
|
||||||
|
if (ctr == ALL_COUNTER) {
|
||||||
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
|
rc = perf_reset_ctr(ctr);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
||||||
|
ec = ioctl(*fds, PERF_EVENT_IOC_RESET, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
||||||
|
{
|
||||||
|
int *fds, ec, rc = -1;
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return -1;
|
||||||
|
*value = 0;
|
||||||
|
|
||||||
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
||||||
|
ec = read(*fds, &val, sizeof(val));
|
||||||
|
if (ec != sizeof(val)) {
|
||||||
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
} else {
|
||||||
|
*value += val;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
211
cpacfstats/stats_sock.c
Normal file
211
cpacfstats/stats_sock.c
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* cpacfstats - display and maintain CPACF perf counters
|
||||||
|
*
|
||||||
|
* basic socket and receive/send functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2015, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "cpacfstats.h"
|
||||||
|
|
||||||
|
|
||||||
|
int open_socket(int mode)
|
||||||
|
{
|
||||||
|
struct sockaddr_un sock_addr;
|
||||||
|
struct group *grp;
|
||||||
|
mode_t m;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
/* group handling */
|
||||||
|
grp = getgrnam(CPACFSTATS_GROUP);
|
||||||
|
if (!grp) {
|
||||||
|
eprint("Getgrnam() failed, group '%s' may not exist on this system ?\n",
|
||||||
|
CPACFSTATS_GROUP);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the client checks for the unix domain socket file */
|
||||||
|
if (mode != SERVER) {
|
||||||
|
if (access(SOCKET_FILE, F_OK) != 0) {
|
||||||
|
eprint("Can't access domain socket file '%s', errno=%d [%s]\n",
|
||||||
|
SOCKET_FILE, errno, strerror(errno));
|
||||||
|
if (errno == ENOENT)
|
||||||
|
eprint("Maybe cpacfstatsd daemon is not running ???\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create socket */
|
||||||
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (s == -1) {
|
||||||
|
eprint("Socket(AF_UNIX,SOCK_STREAM) failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SERVER)
|
||||||
|
remove(SOCKET_FILE);
|
||||||
|
|
||||||
|
memset(&sock_addr, 0, sizeof(sock_addr));
|
||||||
|
sock_addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(sock_addr.sun_path, SOCKET_FILE, sizeof(sock_addr.sun_path));
|
||||||
|
sock_addr.sun_path[sizeof(sock_addr.sun_path)-1] = '\0';
|
||||||
|
|
||||||
|
if (mode == SERVER) {
|
||||||
|
if (bind(s, (struct sockaddr *) &sock_addr,
|
||||||
|
sizeof(struct sockaddr_un)) < 0) {
|
||||||
|
eprint("Bind('%s') failed, errno=%d [%s]\n",
|
||||||
|
SOCKET_FILE, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* change group ownership of the socket file */
|
||||||
|
if (chown(SOCKET_FILE, 0, grp->gr_gid)) {
|
||||||
|
eprint("Chown('%s',...) failed, errno=%d [%s]\n",
|
||||||
|
SOCKET_FILE, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* adapt permissions */
|
||||||
|
m = S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IWGRP|S_IXGRP;
|
||||||
|
if (chmod(SOCKET_FILE, m)) {
|
||||||
|
eprint("Chmod('%s',...) failed, errno=%d [%s]\n",
|
||||||
|
SOCKET_FILE, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* now put the socket into listen state */
|
||||||
|
if (listen(s, BACKLOG) < 0) {
|
||||||
|
eprint("Listen() failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (connect(s, (struct sockaddr *) &sock_addr,
|
||||||
|
sizeof(sock_addr)) < 0) {
|
||||||
|
eprint("Connect() failed, errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int __write(int fd, const void *buf, int buflen)
|
||||||
|
{
|
||||||
|
const unsigned char *p = buf;
|
||||||
|
int n, i = 0;
|
||||||
|
|
||||||
|
while (i < buflen) {
|
||||||
|
n = write(fd, p+i, buflen-i);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
i += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int __read(int fd, void *buf, int buflen)
|
||||||
|
{
|
||||||
|
unsigned char *p = buf;
|
||||||
|
int n, i = 0;
|
||||||
|
|
||||||
|
while (i < buflen) {
|
||||||
|
n = read(fd, p+i, buflen-i);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return n;
|
||||||
|
} else if (n == 0) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
i += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int send_msg(int sfd, struct msg *m)
|
||||||
|
{
|
||||||
|
int n, len;
|
||||||
|
|
||||||
|
len = sizeof(m->head);
|
||||||
|
|
||||||
|
switch (m->head.m_type) {
|
||||||
|
case QUERY:
|
||||||
|
len += sizeof(m->query);
|
||||||
|
break;
|
||||||
|
case ANSWER:
|
||||||
|
len += sizeof(m->answer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
eprint("Unknown type %d\n", m->head.m_type);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = __write(sfd, m, len);
|
||||||
|
if (n != len) {
|
||||||
|
eprint("Write() error: write()=%d expected %d, errno=%d [%s]\n",
|
||||||
|
n, len, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recv_msg(int sfd, struct msg *m)
|
||||||
|
{
|
||||||
|
int n, len;
|
||||||
|
|
||||||
|
len = sizeof(m->head);
|
||||||
|
n = __read(sfd, m, len);
|
||||||
|
if (n != len) {
|
||||||
|
eprint("Recv() error: read()=%d expected %d, errno=%d [%s]\n",
|
||||||
|
n, len, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m->head.m_type) {
|
||||||
|
case QUERY:
|
||||||
|
len = sizeof(m->query);
|
||||||
|
break;
|
||||||
|
case ANSWER:
|
||||||
|
len = sizeof(m->answer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
eprint("Unknown type %d\n", m->head.m_type);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = __read(sfd, ((char *)m) + sizeof(m->head), len);
|
||||||
|
if (n != len) {
|
||||||
|
eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n",
|
||||||
|
n, len, errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
64
cpumf/Makefile
Normal file
64
cpumf/Makefile
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
|
||||||
|
CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf
|
||||||
|
DATA_FILES = cpum-cf-hw-counter.map cpum-cf-generic.ctr \
|
||||||
|
cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \
|
||||||
|
cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \
|
||||||
|
cpum-cf-extended-z13.ctr
|
||||||
|
LIB_FILES = bin/cpumf_helper
|
||||||
|
USRBIN_SCRIPTS = bin/lscpumf
|
||||||
|
USRSBIN_SCRIPTS = bin/chcpumf
|
||||||
|
MAN_FILES = lscpumf.1 chcpumf.8
|
||||||
|
|
||||||
|
all:
|
||||||
|
|
||||||
|
scripts: $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
|
||||||
|
chmod +x $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
|
||||||
|
|
||||||
|
check:
|
||||||
|
|
||||||
|
install: scripts install-man
|
||||||
|
for prg in $(USRBIN_SCRIPTS); do \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \
|
||||||
|
done
|
||||||
|
for prg in $(USRSBIN_SCRIPTS); do \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRSBINDIR) ; \
|
||||||
|
done
|
||||||
|
test -d $(DESTDIR)$(CPUMF_DATADIR) || mkdir -p $(DESTDIR)$(CPUMF_DATADIR)
|
||||||
|
for lib in $(LIB_FILES); do \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$lib $(DESTDIR)$(TOOLS_LIBDIR) ; \
|
||||||
|
done
|
||||||
|
for data in $(DATA_FILES); do \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 data/$$data $(DESTDIR)$(CPUMF_DATADIR) ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
install-man:
|
||||||
|
for man in $(MAN_FILES); do \
|
||||||
|
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D man/$$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
man2pdf:
|
||||||
|
for man in $(MAN_FILES); do \
|
||||||
|
man -t man/$$man |ps2pdf -sPAPERSIZE=a4 - man/$${man}.pdf ; \
|
||||||
|
done
|
||||||
|
man2text:
|
||||||
|
for man in $(MAN_FILES); do \
|
||||||
|
MANWIDTH=80 LANG=C man man/$$man |col -b |expand > man/$${man}.txt ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(LIB_FILES) $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS)
|
||||||
|
|
||||||
|
%: %.in
|
||||||
|
real_libdir=$(TOOLS_LIBDIR); \
|
||||||
|
real_cpumfdatadir=$(CPUMF_DATADIR); \
|
||||||
|
$(SED) -e "s#@lib_path@#$$real_libdir#g" \
|
||||||
|
-e "s#@cpumfdata_path@#$$real_cpumfdatadir#g" \
|
||||||
|
-e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \
|
||||||
|
< $< > $@
|
||||||
|
|
||||||
|
.PHONY: all scripts install install-man man2pdf man2text clean
|
||||||
182
cpumf/bin/chcpumf.in
Normal file
182
cpumf/bin/chcpumf.in
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/perl -W
|
||||||
|
#
|
||||||
|
# chcpumf - Control CPU-measurement facilities
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp. 2014, 2017
|
||||||
|
#
|
||||||
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
#
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use File::Basename qw/fileparse/;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Getopt::Long qw/:config no_ignore_case/;
|
||||||
|
|
||||||
|
# Global constants
|
||||||
|
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
|
||||||
|
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
|
||||||
|
|
||||||
|
# Prototypes
|
||||||
|
sub main();
|
||||||
|
sub show_help();
|
||||||
|
sub show_version();
|
||||||
|
sub do_set_sfb_size($);
|
||||||
|
sub cpumf_set_sfb_size($);
|
||||||
|
sub invoke_cpumf_helper($);
|
||||||
|
|
||||||
|
sub main()
|
||||||
|
{
|
||||||
|
my $config = {
|
||||||
|
# Internal data
|
||||||
|
cpumf => {}, # CPU-MF information hash
|
||||||
|
};
|
||||||
|
|
||||||
|
unless (GetOptions(
|
||||||
|
# General options for help, version, verbose,...
|
||||||
|
"h|help" => \&show_help,
|
||||||
|
"v|version" => \&show_version,
|
||||||
|
"V|verbose+" => \$config->{verbose},
|
||||||
|
# Change specific options
|
||||||
|
"m|min=i" => \$config->{min},
|
||||||
|
"x|max=i" => \$config->{max},
|
||||||
|
)) {
|
||||||
|
print STDERR "One or more options are not valid\n";
|
||||||
|
print STDERR "Try '" . fileparse($0) .
|
||||||
|
" --help' for more information\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect CPU-MF information
|
||||||
|
$config->{cpumf} = invoke_cpumf_helper("-i");
|
||||||
|
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
|
||||||
|
|
||||||
|
# Process parameters
|
||||||
|
my $exitval = 5;
|
||||||
|
if (defined($config->{min}) || defined($config->{max})) {
|
||||||
|
$exitval = do_set_sfb_size($config);
|
||||||
|
} else {
|
||||||
|
print STDERR "You must specify a valid option\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit($exitval);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub show_help()
|
||||||
|
{
|
||||||
|
my $prog = fileparse($0);
|
||||||
|
|
||||||
|
print <<"EoHelp";
|
||||||
|
Usage: chcpumf -h|-v
|
||||||
|
chcpumf -m <num_sdb>
|
||||||
|
chcpumf -x <num_sdb>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-m <num_sdb> Specifies the initial size of the sampling buffer.
|
||||||
|
A sample-data-block (SDB) consumes about 4 kilobytes.
|
||||||
|
-x <num_sdb> Specifies the maximum size of the sampling buffer.
|
||||||
|
A sample-data-block (SDB) consumes about 4 kilobytes.
|
||||||
|
-h Displays help information, then exits.
|
||||||
|
-v Displays version information, then exits.
|
||||||
|
|
||||||
|
For more help information, issue 'man $prog'.
|
||||||
|
EoHelp
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub show_version()
|
||||||
|
{
|
||||||
|
print <<'EoVersion';
|
||||||
|
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
|
||||||
|
Copyright IBM Corp. 2014, 2017
|
||||||
|
|
||||||
|
EoVersion
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_set_sfb_size($)
|
||||||
|
{
|
||||||
|
my @size = @{shift()};
|
||||||
|
my $val = join ',', @size[0,1];
|
||||||
|
my ($SFBSIZE, $rc);
|
||||||
|
|
||||||
|
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
|
||||||
|
# Check the return code of print and close to detect error conditions
|
||||||
|
# reported by the device driver. Because perl might buffer data, print
|
||||||
|
# might be successful, but close might then report the error condition.
|
||||||
|
# So check the return code of both functions and always close the file
|
||||||
|
# handle.
|
||||||
|
$rc = print { $SFBSIZE } "$val\n";
|
||||||
|
unless ($rc) {
|
||||||
|
close($SFBSIZE);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
return close($SFBSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_set_sfb_size($)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
my $size;
|
||||||
|
|
||||||
|
# Check if sampling facility is available
|
||||||
|
unless (exists $c->{cpumf}->{sf}) {
|
||||||
|
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
# Check if perf support is available
|
||||||
|
unless (exists $c->{cpumf}->{sf}->{perf}) {
|
||||||
|
print STDERR "No perf support for the CPU-measurement" .
|
||||||
|
" sampling facility availalble\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optionally, change the sampling buffer sizes
|
||||||
|
if (defined($c->{min}) || defined($c->{max})) {
|
||||||
|
$size = invoke_cpumf_helper("--sfb-size");
|
||||||
|
# Use current size value for zero min/max specifications
|
||||||
|
$size->[0] = $c->{min} if defined($c->{min});
|
||||||
|
$size->[1] = $c->{max} if defined($c->{max});
|
||||||
|
# Validate new settings
|
||||||
|
if ($size->[0] < 1 || $size->[1] < 1) {
|
||||||
|
die "The specified number(s) are not valid\n";
|
||||||
|
}
|
||||||
|
if ($size->[0] >= $size->[1]) {
|
||||||
|
die "The specified maximum must be greater " .
|
||||||
|
"than the minimum\n";
|
||||||
|
}
|
||||||
|
# Set new sampling buffer sizes
|
||||||
|
unless (cpumf_set_sfb_size($size)) {
|
||||||
|
die "Failed to change sampling buffer size: $!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finally, show sampling buffer sizes
|
||||||
|
if ($c->{verbose}) {
|
||||||
|
$size = invoke_cpumf_helper("--sfb-size");
|
||||||
|
print "Sampling buffer sizes:\n";
|
||||||
|
printf " Minimum: %6u sample-data-blocks\n", $size->[0];
|
||||||
|
printf " Maximum: %6u sample-data-blocks\n", $size->[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub invoke_cpumf_helper($)
|
||||||
|
{
|
||||||
|
my $parms = shift();
|
||||||
|
my $result;
|
||||||
|
|
||||||
|
# Call helper module
|
||||||
|
my $output = qx"$CPUMF_HELPER $parms";
|
||||||
|
die "Failed to run helper module for '$parms'\n" if $? >> 8;
|
||||||
|
$result = eval "$output";
|
||||||
|
die "Failed to parse helper module data\n" if $@;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
&main();
|
||||||
|
__DATA__
|
||||||
|
__END__
|
||||||
483
cpumf/bin/cpumf_helper.in
Normal file
483
cpumf/bin/cpumf_helper.in
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
#!/usr/bin/perl -W
|
||||||
|
#
|
||||||
|
# cpumf_helper - Helper module for managing CPU-measurement facilities (CPU-MF)
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp. 2014, 2017
|
||||||
|
#
|
||||||
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
#
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Carp qw/croak/;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Getopt::Long qw/:config no_ignore_case/;
|
||||||
|
use Pod::Usage;
|
||||||
|
|
||||||
|
|
||||||
|
# Global constants
|
||||||
|
my $SERVICE_LEVELS = '/proc/service_levels';
|
||||||
|
my $CPUMF_DATA_DIR = '@cpumfdata_path@';
|
||||||
|
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
|
||||||
|
my $CPUM_SF_DBF='/sys/kernel/debug/s390dbf/cpum_sf';
|
||||||
|
|
||||||
|
# Counter set bits (according to QUERY COUNTER INFORMATION)
|
||||||
|
my $BASIC_SET = 0x0002;
|
||||||
|
my $PROBLEM_STATE_SET = 0x0004;
|
||||||
|
my $CRYPTO_SET = 0x0008;
|
||||||
|
my $EXTENTED_SET = 0x0001;
|
||||||
|
my $MT_DIAG_SET = 0x0020;
|
||||||
|
my $COPROC_GRP_SET = 0x8000;
|
||||||
|
|
||||||
|
|
||||||
|
# Public prototypes
|
||||||
|
sub cpumf_collect_data();
|
||||||
|
sub cpumf_get_sfb_size();
|
||||||
|
sub cpumf_set_sfb_size($);
|
||||||
|
sub cpumf_parse_ctrdef($;$);
|
||||||
|
sub cpumf_load_ctrdef($;$);
|
||||||
|
sub cpumf_get_counter_set($);
|
||||||
|
sub cpumf_counter_set_names();
|
||||||
|
sub cpumf_counter_set_ids();
|
||||||
|
sub cpumf_hardware_counter_map();
|
||||||
|
|
||||||
|
# Internal prototypes
|
||||||
|
sub cpumf_parse_cf($$);
|
||||||
|
sub cpumf_parse_sf($$);
|
||||||
|
|
||||||
|
|
||||||
|
sub cpumf_get_sfb_size()
|
||||||
|
{
|
||||||
|
my $val = "0,0";
|
||||||
|
my $SFBSIZE;
|
||||||
|
|
||||||
|
return $val unless open($SFBSIZE, '<', $CPUM_SFB_SIZE);
|
||||||
|
$val = <$SFBSIZE>;
|
||||||
|
chomp($val);
|
||||||
|
close($SFBSIZE);
|
||||||
|
|
||||||
|
return [split /,/, $val];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_set_sfb_size($)
|
||||||
|
{
|
||||||
|
my @size = @{shift()};
|
||||||
|
my $val = join ',', @size[0,1];
|
||||||
|
my ($SFBSIZE, $rc);
|
||||||
|
|
||||||
|
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
|
||||||
|
# Check the return code of print and close to detect error conditions
|
||||||
|
# reported by the device driver. Because perl might buffer data, print
|
||||||
|
# might be successful, but close might then report the error condition.
|
||||||
|
# So check the return code of both functions and always close the file
|
||||||
|
# handle.
|
||||||
|
$rc = print { $SFBSIZE } "$val\n";
|
||||||
|
unless ($rc) {
|
||||||
|
close($SFBSIZE);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
return close($SFBSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub cpumf_parse_cf($$)
|
||||||
|
{
|
||||||
|
my ($ent, $data) = @_;
|
||||||
|
|
||||||
|
if ($ent =~ /version=([0-9.]+) authorization=([[:xdigit:]]+)/) {
|
||||||
|
$data->{cf} = { version => $1, auth => hex($2) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_parse_sf($$)
|
||||||
|
{
|
||||||
|
my ($ent, $data) = @_;
|
||||||
|
|
||||||
|
# Parse common sampling facility entry
|
||||||
|
if ($ent =~ /min_rate=(\d+) max_rate=(\d+) cpu_speed=(\d+)/) {
|
||||||
|
$data->{sf} = {
|
||||||
|
min_sampl_interval => $1,
|
||||||
|
max_sampl_interval => $2,
|
||||||
|
cpu_speed => $3,
|
||||||
|
# This contains a list of authorized sampling modes, for
|
||||||
|
# example, basic
|
||||||
|
modes => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse sampling facility mode entries
|
||||||
|
if ($ent =~ /mode=(\w+) sample_size=(\d+)/) {
|
||||||
|
$data->{sf}->{modes}->{$1}->{sample_size} = $2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_collect_data()
|
||||||
|
{
|
||||||
|
my $SL;
|
||||||
|
|
||||||
|
# Collect CPU-MF information from /proc/service_levels
|
||||||
|
return undef unless open($SL, '<', $SERVICE_LEVELS);
|
||||||
|
my @sl = <$SL>;
|
||||||
|
chomp(@sl);
|
||||||
|
close($SL);
|
||||||
|
|
||||||
|
# Process CPU-MF information and build data hash
|
||||||
|
my $data = {};
|
||||||
|
foreach my $ent (@sl) {
|
||||||
|
$ent =~ s/^CPU-MF: // or next;
|
||||||
|
cpumf_parse_cf($ent, $data) if $ent =~ s/Counter facility: //;
|
||||||
|
cpumf_parse_sf($ent, $data) if $ent =~ s/Sampling facility: //;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect perf support for available facilities
|
||||||
|
if (-e '/sys/bus/event_source/devices/cpum_cf') {
|
||||||
|
$data->{cf}->{perf} = "cpum_cf" if exists $data->{cf};
|
||||||
|
}
|
||||||
|
if (-e '/sys/bus/event_source/devices/cpum_sf') {
|
||||||
|
$data->{sf}->{perf} = "cpum_sf" if exists $data->{sf};
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse the specified counter definition file and returns a hash containing
|
||||||
|
# the parsed counter definition. The optional argument specifies a hash
|
||||||
|
# reference to which the new definitions are added. This reference is returned.
|
||||||
|
sub cpumf_parse_ctrdef($;$)
|
||||||
|
{
|
||||||
|
my $ctrdef = shift();
|
||||||
|
my $h = @_ ? shift() : {};
|
||||||
|
my $CTRDEF;
|
||||||
|
|
||||||
|
return undef unless open($CTRDEF, '<', "$CPUMF_DATA_DIR/$ctrdef");
|
||||||
|
my ($ctr, $name);
|
||||||
|
while (my $line = <$CTRDEF>) {
|
||||||
|
next if $line =~ /^#/;
|
||||||
|
chomp($line);
|
||||||
|
|
||||||
|
# Parse start of counter definition entry
|
||||||
|
if ($line =~ m/^Counter:\s*(0x[[:xdigit:]]+|\d+)
|
||||||
|
\s+Name:\s*([[:alnum:]_]+)$/x) {
|
||||||
|
($ctr, $name) = ($1, $2);
|
||||||
|
$ctr = hex($ctr) if $ctr =~ /^0x/;
|
||||||
|
unless (length($ctr)) {
|
||||||
|
print STDERR "Found invalid entry in counter " .
|
||||||
|
"definition: line $.\n";
|
||||||
|
}
|
||||||
|
$h->{$ctr} = { name => $name || "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
# At this point, a counter must be defined
|
||||||
|
next unless defined($ctr);
|
||||||
|
|
||||||
|
# Parse short description (optional)
|
||||||
|
if ($line =~ m/^Short-Description:\s*(\S.*)?$/) {
|
||||||
|
$h->{ctr}->{shortdesc} = $1 || "";
|
||||||
|
|
||||||
|
# Parse start of counter description
|
||||||
|
} elsif ($line =~ m/^Description:\s*(\S.*)?$/) {
|
||||||
|
$h->{$ctr}->{desc} = $1 || "";
|
||||||
|
|
||||||
|
# Parse end of counter description
|
||||||
|
} elsif ($line =~ m/^\.$/) {
|
||||||
|
# Complete the counter definition
|
||||||
|
$h->{$ctr}->{set} = cpumf_get_counter_set($ctr);
|
||||||
|
# Trim whitespaces
|
||||||
|
$h->{$ctr}->{shortdesc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{shortdesc};
|
||||||
|
$h->{$ctr}->{desc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{desc};
|
||||||
|
# Finally, reset counter for next entry
|
||||||
|
$ctr = undef;
|
||||||
|
|
||||||
|
# Line is part of counter description (if $ctr_num is set)
|
||||||
|
} else {
|
||||||
|
$h->{$ctr}->{desc} .= " $line";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close($CTRDEF);
|
||||||
|
|
||||||
|
return $h;
|
||||||
|
}
|
||||||
|
|
||||||
|
# IBM System z hardware with CPU-M counter facility support
|
||||||
|
my $system_z_hwtype_map = {
|
||||||
|
# Machine type Description
|
||||||
|
'' => 'Unknown hardware model',
|
||||||
|
2097 => 'IBM System z10 EC',
|
||||||
|
2098 => 'IBM System z10 BC',
|
||||||
|
2817 => 'IBM zEnterprise 196',
|
||||||
|
2818 => 'IBM zEnterprise 114',
|
||||||
|
2827 => 'IBM zEnterprise EC12',
|
||||||
|
2828 => 'IBM zEnterprise BC12',
|
||||||
|
2964 => 'IBM z13',
|
||||||
|
2965 => 'IBM z13s',
|
||||||
|
};
|
||||||
|
|
||||||
|
sub get_hardware_type()
|
||||||
|
{
|
||||||
|
my $type = "";
|
||||||
|
my $SYSINFO;
|
||||||
|
|
||||||
|
return undef unless open($SYSINFO, '<', '/proc/sysinfo');
|
||||||
|
while (my $line = <$SYSINFO>) {
|
||||||
|
if ($line =~ m/^Type:\s*(\d+)\s*$/) {
|
||||||
|
$type = $1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close($SYSINFO);
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_load_ctrdef($;$)
|
||||||
|
{
|
||||||
|
my $hw_type = shift();
|
||||||
|
my $authorized = @_ ? shift() : 0xffff; # Counter Set authorization
|
||||||
|
|
||||||
|
my $ctrmap = cpumf_hardware_counter_map();
|
||||||
|
return unless $ctrmap;
|
||||||
|
|
||||||
|
my $h = {};
|
||||||
|
# Load generic counter sets
|
||||||
|
cpumf_parse_ctrdef($ctrmap->{0}, $h) or
|
||||||
|
croak "Failed to read generic counter definition: $!\n";
|
||||||
|
# Load hardware model specific counter set(s)
|
||||||
|
if ($hw_type && $ctrmap->{$hw_type}) {
|
||||||
|
# Hardware-model specific counter sets are:
|
||||||
|
# - Extended Counter Set
|
||||||
|
# - MT-diagnostic Counter Set
|
||||||
|
cpumf_parse_ctrdef($ctrmap->{$hw_type}, $h) or
|
||||||
|
croak "Failed to read hardware-model counter definition: $!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove counter sets that miss authorizations
|
||||||
|
my @no_auth_list = ();
|
||||||
|
foreach my $ctr (sort keys %$h) {
|
||||||
|
push @no_auth_list, $ctr unless $h->{$ctr}->{set} & $authorized;
|
||||||
|
}
|
||||||
|
delete $h->{$_} foreach (@no_auth_list);
|
||||||
|
|
||||||
|
return $h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub cpumf_get_counter_set($)
|
||||||
|
{
|
||||||
|
my $ctr = shift();
|
||||||
|
|
||||||
|
return $BASIC_SET if $ctr < 32;
|
||||||
|
return $PROBLEM_STATE_SET if $ctr < 64;
|
||||||
|
return $CRYPTO_SET if $ctr < 128;
|
||||||
|
#
|
||||||
|
# The extended counter set ranges from
|
||||||
|
# 128 to
|
||||||
|
# 159 for csvn == 1
|
||||||
|
# 175 for csvn == 2
|
||||||
|
# 255 for csvn > 2
|
||||||
|
# Tolerate any future counters up to
|
||||||
|
# the MT-diagnostic counter set.
|
||||||
|
return $EXTENTED_SET if $ctr < 448;
|
||||||
|
#
|
||||||
|
# The MT-diagnostic counter set ranges from
|
||||||
|
# 448 to
|
||||||
|
# 495 for cvsn > 3
|
||||||
|
return $MT_DIAG_SET if $ctr <= 495;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_counter_set_names()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
# Identifier Name
|
||||||
|
$BASIC_SET => 'Basic Counter Set',
|
||||||
|
$PROBLEM_STATE_SET => 'Problem-State Counter Set',
|
||||||
|
$CRYPTO_SET => 'Crypto-Activity Counter Set',
|
||||||
|
$EXTENTED_SET => 'Extended Counter Set',
|
||||||
|
$MT_DIAG_SET => 'MT-diagnostic Counter Set',
|
||||||
|
$COPROC_GRP_SET => 'Coprocessor Group Counter Set',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_counter_set_ids()
|
||||||
|
{
|
||||||
|
return [$BASIC_SET, $PROBLEM_STATE_SET, $CRYPTO_SET, $EXTENTED_SET,
|
||||||
|
$MT_DIAG_SET, $COPROC_GRP_SET];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cpumf_hardware_counter_map()
|
||||||
|
{
|
||||||
|
my $map = do "$CPUMF_DATA_DIR/cpum-cf-hw-counter.map";
|
||||||
|
croak "Failed to parse mapfile: $@" if $@;
|
||||||
|
croak "Failed to read mapfile: $!" unless defined $map;
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub cpumf_helper_main()
|
||||||
|
{
|
||||||
|
# Configuration settings and options
|
||||||
|
my $conf = {
|
||||||
|
};
|
||||||
|
|
||||||
|
# Parse command line option
|
||||||
|
GetOptions(
|
||||||
|
"i|info" => \$conf->{opt_info},
|
||||||
|
"c|counter=i" => \$conf->{opt_ctr},
|
||||||
|
"ctr-def=i" => \$conf->{opt_ctrdef},
|
||||||
|
"hardware-type" => \$conf->{opt_hwtype},
|
||||||
|
"ctr-set-names" => \$conf->{opt_ctrset_names},
|
||||||
|
"ctr-set-ids" => \$conf->{opt_ctrset_ids},
|
||||||
|
"sfb-size" => \$conf->{opt_sfb_size},
|
||||||
|
"ctr-sf" => \$conf->{opt_sf_ctr},
|
||||||
|
) or pod2usage(-message =>"One or more options are not valid",
|
||||||
|
-exitval => 1);
|
||||||
|
|
||||||
|
# Setting up Data::Dumper to create parseable Perl output
|
||||||
|
local $Data::Dumper::Purity = 1;
|
||||||
|
local $Data::Dumper::Sortkeys = 1;
|
||||||
|
local $Data::Dumper::Terse = 1;
|
||||||
|
|
||||||
|
###print STDERR "CONF: " . Dumper($conf) . "\n";
|
||||||
|
|
||||||
|
# Process command line options
|
||||||
|
my $exitval = 0;
|
||||||
|
my $result;
|
||||||
|
if (defined($conf->{opt_info})) {
|
||||||
|
$result = cpumf_collect_data();
|
||||||
|
|
||||||
|
# Display System z hardware type
|
||||||
|
} elsif (defined($conf->{opt_hwtype})) {
|
||||||
|
my $type = get_hardware_type();
|
||||||
|
$result = [$type, $system_z_hwtype_map->{$type} || ""];
|
||||||
|
|
||||||
|
# Display counters for current System z hardware
|
||||||
|
} elsif (defined($conf->{opt_ctr})) {
|
||||||
|
my $type = get_hardware_type();
|
||||||
|
$type = 0 unless $type;
|
||||||
|
$result = cpumf_load_ctrdef($type, $conf->{opt_ctr});
|
||||||
|
|
||||||
|
# Display counters for a particular System z hardware type
|
||||||
|
} elsif (defined($conf->{opt_ctrdef})) {
|
||||||
|
my $m = cpumf_hardware_counter_map();
|
||||||
|
if (exists $m->{$conf->{opt_ctrdef}}) {
|
||||||
|
$result = cpumf_parse_ctrdef($m->{$conf->{opt_ctrdef}});
|
||||||
|
} else {
|
||||||
|
printf STDERR "Invalid counter definition\n";
|
||||||
|
$exitval = 2;
|
||||||
|
}
|
||||||
|
# Display the size of the sampling facility buffer (sfb)
|
||||||
|
} elsif (defined($conf->{opt_sfb_size})) {
|
||||||
|
$result = cpumf_get_sfb_size();
|
||||||
|
|
||||||
|
# Display counter definitions for the sampling facility support (perf)
|
||||||
|
} elsif (defined($conf->{opt_sf_ctr})) {
|
||||||
|
$result = cpumf_parse_ctrdef('cpum-sf-modes.ctr');
|
||||||
|
|
||||||
|
# Display mapping of counter set IDs to counter set names
|
||||||
|
} elsif (defined($conf->{opt_ctrset_names})) {
|
||||||
|
$result = cpumf_counter_set_names();
|
||||||
|
|
||||||
|
# Display counter set IDs
|
||||||
|
} elsif (defined($conf->{opt_ctrset_ids})) {
|
||||||
|
$result = cpumf_counter_set_ids();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pod2usage(-message => "No option specified",
|
||||||
|
-exitval => 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display result
|
||||||
|
print Dumper($result) if $result;
|
||||||
|
exit $exitval;
|
||||||
|
}
|
||||||
|
|
||||||
|
&cpumf_helper_main();
|
||||||
|
__DATA__
|
||||||
|
__END__
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
B<cpumf_helper> - Helper module for managing CPU-Measurement Facilities (CPU-MF)
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The B<cpumf_helper> program is not intended for ordinary use.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<-i>, B<--info>
|
||||||
|
|
||||||
|
Displays detailed information about installed and available CPU-measurement
|
||||||
|
facilities and the related Linux support.
|
||||||
|
|
||||||
|
=item B<-c>, B<--counter> I<authorization_value>
|
||||||
|
|
||||||
|
Displays counter information for the current System z hardware. The
|
||||||
|
authorization value provides information about the authorized counter sets. The
|
||||||
|
value must be specified in decimal format.
|
||||||
|
|
||||||
|
To display all supported counters, specify C<65535> (FFFF hex).
|
||||||
|
|
||||||
|
To display supported counters for a particular System z hardware, use the
|
||||||
|
B<--ctr-def> option and specify the System z hardware type.
|
||||||
|
|
||||||
|
=item B<--hardware-type>
|
||||||
|
|
||||||
|
Displays the System z hardware type.
|
||||||
|
|
||||||
|
=item B<--ctr-def> I<hardware_type>
|
||||||
|
|
||||||
|
Displays detailed information about a particular counter set for the specified
|
||||||
|
System z hardware type, I<hardware_type>. If you specify zero for
|
||||||
|
I<hardware_type>, type-independent counter sets are displayed.
|
||||||
|
|
||||||
|
=item B<--ctr-set-names>
|
||||||
|
|
||||||
|
Displays the mapping of counter set IDs to counter set names. The counter set
|
||||||
|
IDs are numbers that are used in counter definitions.
|
||||||
|
|
||||||
|
=item B<--ctr-set-ids>
|
||||||
|
|
||||||
|
Displays the counter set IDs. The counter set IDs are numbers that match the
|
||||||
|
authorization bits (see QUERY COUNTER INFORMATION). The output format is a
|
||||||
|
list. To get the ID for a particular counter set, use an index number as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
=over 16
|
||||||
|
|
||||||
|
=item 0: Basic Counter Set
|
||||||
|
|
||||||
|
=item 1: Problem-State Counter Set
|
||||||
|
|
||||||
|
=item 2: Crypto-Activity Counter Set
|
||||||
|
|
||||||
|
=item 3: Extended Counter Set
|
||||||
|
|
||||||
|
=item 4: MT-diagnostic Counter Set
|
||||||
|
|
||||||
|
=item 5: Coprocessor Group Counter Set
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item B<--ctr-sf>
|
||||||
|
|
||||||
|
Displays the counter definitions for the sampling facility support. These
|
||||||
|
counter definitions are specific to Linux perf infrastructure.
|
||||||
|
|
||||||
|
=item B<--sfb-size>
|
||||||
|
|
||||||
|
Displays the size of the sampling facility buffer (SFB). The minimum and
|
||||||
|
maximum numbers are measured in units of sample-data-blocks. A
|
||||||
|
sample-data-block uses about 4 kilobytes.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 FILES
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<lscpumf(1)>, L<chcpumf(1)>
|
||||||
|
|
||||||
|
=cut
|
||||||
387
cpumf/bin/lscpumf.in
Normal file
387
cpumf/bin/lscpumf.in
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
#!/usr/bin/perl -W
|
||||||
|
#
|
||||||
|
# lscpumf - Display information about CPU-measurement facilities
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp. 2014, 2017
|
||||||
|
#
|
||||||
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
#
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Data::Dumper;
|
||||||
|
use File::Basename qw/fileparse/;
|
||||||
|
use Getopt::Long qw/:config no_ignore_case/;
|
||||||
|
|
||||||
|
# Global constants
|
||||||
|
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
|
||||||
|
|
||||||
|
# Prototypes
|
||||||
|
sub main();
|
||||||
|
sub show_help();
|
||||||
|
sub show_version();
|
||||||
|
sub do_show_info($);
|
||||||
|
sub do_show_cf($);
|
||||||
|
sub do_show_sf($);
|
||||||
|
sub do_show_ctr($;$);
|
||||||
|
sub do_show_sf_events($);
|
||||||
|
sub invoke_cpumf_helper($);
|
||||||
|
|
||||||
|
|
||||||
|
sub main()
|
||||||
|
{
|
||||||
|
my $config = {
|
||||||
|
# Internal data
|
||||||
|
cpumf => {}, # CPU-MF information hash
|
||||||
|
};
|
||||||
|
|
||||||
|
unless (GetOptions(
|
||||||
|
# General options for help, version,...
|
||||||
|
"h|help" => \&show_help,
|
||||||
|
"v|version" => \&show_version,
|
||||||
|
# Display options
|
||||||
|
"i|info" => \$config->{opt_info},
|
||||||
|
"c|list-counters" => \$config->{opt_ctr},
|
||||||
|
"C|list-all-counters" => \$config->{opt_ctr_all},
|
||||||
|
"s|list-sampling-events" => \$config->{opt_ctr_sf},
|
||||||
|
)) {
|
||||||
|
print STDERR "One or more options are not valid\n";
|
||||||
|
print STDERR "Try '" . fileparse($0) .
|
||||||
|
" --help' for more information\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect CPU-MF information
|
||||||
|
$config->{cpumf} = invoke_cpumf_helper("-i");
|
||||||
|
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
|
||||||
|
|
||||||
|
# Process parameters
|
||||||
|
my $exitval = 5;
|
||||||
|
if (defined($config->{opt_info})) {
|
||||||
|
do_show_cf($config);
|
||||||
|
do_show_sf($config);
|
||||||
|
$exitval = 0;
|
||||||
|
|
||||||
|
} elsif (defined($config->{opt_ctr})) {
|
||||||
|
$exitval = do_show_ctr($config);
|
||||||
|
} elsif (defined($config->{opt_ctr_all})) {
|
||||||
|
$exitval = do_show_ctr($config, "all")
|
||||||
|
} elsif (defined($config->{opt_ctr_sf})) {
|
||||||
|
$exitval = do_show_sf_events($config);
|
||||||
|
} else {
|
||||||
|
$exitval = do_show_info($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit($exitval);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub show_help()
|
||||||
|
{
|
||||||
|
my $prog = fileparse($0);
|
||||||
|
|
||||||
|
print <<"EoHelp";
|
||||||
|
Usage: lscpumf -h|-v
|
||||||
|
lscpumf [-i]
|
||||||
|
lscpumf -c|-C
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-i Displays detailed information.
|
||||||
|
-c Lists counters for which the LPAR is authorized.
|
||||||
|
-C Lists counters regardless of LPAR authorization.
|
||||||
|
-s Lists perf raw events that activate the sampling facility.
|
||||||
|
-h Displays help information, then exits.
|
||||||
|
-v Displays version information, then exits.
|
||||||
|
|
||||||
|
For more help information, issue 'man $prog'.
|
||||||
|
EoHelp
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub show_version()
|
||||||
|
{
|
||||||
|
print <<'EoVersion';
|
||||||
|
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
|
||||||
|
Copyright IBM Corp. 2014, 2017
|
||||||
|
|
||||||
|
EoVersion
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_info($)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
my $cpumf = $c->{cpumf};
|
||||||
|
my @f = ();
|
||||||
|
|
||||||
|
push @f, "CPU-measurement Counter Facility" if exists $cpumf->{cf};
|
||||||
|
push @f, "CPU-measurement Sampling Facility" if exists $cpumf->{sf};
|
||||||
|
|
||||||
|
if (@f) {
|
||||||
|
print((join "\n", @f) . "\n");
|
||||||
|
} else {
|
||||||
|
print STDERR "No CPU-measurement facilities detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_cf($)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
|
||||||
|
# Check if counter facility is available
|
||||||
|
unless (exists $c->{cpumf}->{cf}) {
|
||||||
|
print STDERR "No CPU-measurement counter facility detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retrieve counter facility information
|
||||||
|
my $cf = $c->{cpumf}->{cf};
|
||||||
|
|
||||||
|
# Create list of authorized counter sets
|
||||||
|
my @sets = ();
|
||||||
|
push @sets, "None" unless $cf->{auth};
|
||||||
|
push @sets, "Crypto-Activity counter set" if $cf->{auth} & 0x8;
|
||||||
|
push @sets, "Problem-State counter set" if $cf->{auth} & 0x4;
|
||||||
|
push @sets, "Basic counter set" if $cf->{auth} & 0x2;
|
||||||
|
push @sets, "Extented counter set" if $cf->{auth} & 0x1;
|
||||||
|
push @sets, "MT-diagnostic counter set" if $cf->{auth} & 0x20;
|
||||||
|
|
||||||
|
print "CPU-measurement counter facility\n";
|
||||||
|
print "-" x 74 . "\n";
|
||||||
|
# TODO Display additional information about available conters depending
|
||||||
|
# on the version information
|
||||||
|
print "Version: " . $cf->{version} . "\n";
|
||||||
|
print "\n";
|
||||||
|
print "Authorized counter sets:\n";
|
||||||
|
print " $_\n" foreach (sort @sets);
|
||||||
|
printf "\nLinux perf event support: %s\n\n",
|
||||||
|
exists $cf->{perf} ? "Yes (PMU: $cf->{perf})" : "No";
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub div_ceil($$)
|
||||||
|
{
|
||||||
|
my ($a, $b) = @_;
|
||||||
|
return int(($a + $b - 1) / $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub humanize_bytes($;$)
|
||||||
|
{
|
||||||
|
my $bytes = shift();
|
||||||
|
my @units = split //, " KMGTPEZY";
|
||||||
|
|
||||||
|
my $u = @_ ? shift() : 0;
|
||||||
|
while ($bytes >= 1024 && $u <= $#units) {
|
||||||
|
$bytes /= 1024;
|
||||||
|
$u++;
|
||||||
|
}
|
||||||
|
return sprintf "%.f%sB", $bytes, $units[$u];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_sfb_details($)
|
||||||
|
{
|
||||||
|
my $n_sdb = shift();
|
||||||
|
|
||||||
|
# Calculate sampling buffer structure
|
||||||
|
my $n_sdbt = div_ceil($n_sdb, 511);
|
||||||
|
my $n_pages = $n_sdb + $n_sdbt;
|
||||||
|
return [
|
||||||
|
$n_sdb, # number of sample-data-blocks
|
||||||
|
$n_sdbt, # number of sample-data-block-tables
|
||||||
|
$n_pages, # number of 4K pages
|
||||||
|
$n_pages * 4096, # size in bytes
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_sf($)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
|
||||||
|
# Check if sampling facility is available
|
||||||
|
unless (exists $c->{cpumf}->{sf}) {
|
||||||
|
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
my $sf = $c->{cpumf}->{sf};
|
||||||
|
my $size = invoke_cpumf_helper("--sfb-size");
|
||||||
|
|
||||||
|
# Sampling facility information
|
||||||
|
print "CPU-measurement sampling facility\n";
|
||||||
|
print "-" x 74 . "\n";
|
||||||
|
print "Sampling Interval:\n";
|
||||||
|
printf " Minimum: %10u cycles (approx. %8u Hz)\n",
|
||||||
|
$sf->{min_sampl_interval},
|
||||||
|
1000000 * $sf->{cpu_speed} / $sf->{min_sampl_interval};
|
||||||
|
printf " Maximum: %10u cycles (approx. %8u Hz)\n",
|
||||||
|
$sf->{max_sampl_interval},
|
||||||
|
1000000 * $sf->{cpu_speed} / $sf->{max_sampl_interval};
|
||||||
|
print "\n";
|
||||||
|
print "Authorized sampling modes:\n";
|
||||||
|
foreach my $m (sort keys %{$sf->{modes}}) {
|
||||||
|
printf " %-10s (sample size: %3u bytes)\n", $m,
|
||||||
|
$sf->{modes}->{$m}->{sample_size};
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
printf "\nLinux perf event support: %s\n\n",
|
||||||
|
exists $sf->{perf} ? "Yes (PMU: $sf->{perf})" : "No";
|
||||||
|
|
||||||
|
# Sampling buffer settings for cpum_sf
|
||||||
|
goto out unless exists $sf->{perf};
|
||||||
|
|
||||||
|
print "Current sampling buffer settings for $sf->{perf}:\n";
|
||||||
|
printf " Basic-sampling mode\n";
|
||||||
|
my $s = get_sfb_details($size->[0]);
|
||||||
|
printf " Minimum: %6u sample-data-blocks (%6s)\n",
|
||||||
|
$s->[0], humanize_bytes($s->[3]);
|
||||||
|
$s = get_sfb_details($size->[1]);
|
||||||
|
printf " Maximum: %6u sample-data-blocks (%6s)\n",
|
||||||
|
$s->[0], humanize_bytes($s->[3]);
|
||||||
|
unless (exists $sf->{modes}->{diagnostic}) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sampling buffer setting specific to diagnostic-sampling mode
|
||||||
|
my $f_diag = div_ceil($sf->{modes}->{diagnostic}->{sample_size},
|
||||||
|
$sf->{modes}->{basic}->{sample_size});
|
||||||
|
print "\n";
|
||||||
|
printf " Diagnostic-sampling mode (including basic-sampling)\n";
|
||||||
|
$s = get_sfb_details($size->[0] * $f_diag);
|
||||||
|
printf " Minimum: %6u sample-data-blocks (%6s)\n",
|
||||||
|
$s->[0], humanize_bytes($s->[3]);
|
||||||
|
$s = get_sfb_details($size->[1] * $f_diag);
|
||||||
|
printf " Maximum: %6u sample-data-blocks (%6s)\n",
|
||||||
|
$s->[0], humanize_bytes($s->[3]);
|
||||||
|
printf " Size factor: %2u\n", $f_diag;
|
||||||
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_counters($$)
|
||||||
|
{
|
||||||
|
my ($ctrdef, $header) = @_;
|
||||||
|
my $set_name_map = invoke_cpumf_helper('--ctr-set-names');
|
||||||
|
my $out = [];
|
||||||
|
|
||||||
|
my ($ctr_perf, $ctr_num, $set, $name, $desc);
|
||||||
|
format PERF_CTR_FORM =
|
||||||
|
r@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||||
|
$ctr_perf, $name
|
||||||
|
|
||||||
|
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||||
|
$desc
|
||||||
|
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
|
||||||
|
$desc
|
||||||
|
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||||
|
$set
|
||||||
|
|
||||||
|
.
|
||||||
|
print $header;
|
||||||
|
$~ = "PERF_CTR_FORM";
|
||||||
|
foreach my $ctr (sort { $a <=> $b } keys %$ctrdef) {
|
||||||
|
$ctr_perf = sprintf "%x", $ctr;
|
||||||
|
$ctr_num = $ctr < (1 << 16) ? $ctr : "";
|
||||||
|
$name = $ctrdef->{$ctr}->{name};
|
||||||
|
$desc = $ctrdef->{$ctr}->{shortdesc} ?
|
||||||
|
$ctrdef->{$ctr}->{shortdesc}
|
||||||
|
: $ctrdef->{$ctr}->{desc};
|
||||||
|
$desc .= ".";
|
||||||
|
$set = $set_name_map->{$ctrdef->{$ctr}->{set}};
|
||||||
|
if ($set) {
|
||||||
|
$set = "Counter $ctr_num / $set.";
|
||||||
|
} else {
|
||||||
|
$set = "This event is not associated with a counter set.";
|
||||||
|
}
|
||||||
|
write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_ctr($;$)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
|
||||||
|
# Check if counter facility is available
|
||||||
|
unless (exists $c->{cpumf}->{cf}) {
|
||||||
|
print STDERR "No CPU-measurement counter facility detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retrieve counter authorization ("all" or authorized counters only)
|
||||||
|
my $auth = @_ ? hex("0xFFFF") : $c->{cpumf}->{cf}->{auth};
|
||||||
|
|
||||||
|
# Retrieve counter information
|
||||||
|
my $ctrs = invoke_cpumf_helper("-c $auth");
|
||||||
|
unless ($ctrs) {
|
||||||
|
print STDERR "No counters are available or authorized\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retrieve hardware type
|
||||||
|
my $hwtype = invoke_cpumf_helper("--hardware-type");
|
||||||
|
my $model = length $hwtype->[1] ? "for $hwtype->[1]" : "";
|
||||||
|
my $header = <<"EoHeader";
|
||||||
|
Perf event counter list $model
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Raw
|
||||||
|
event Name Description
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
EoHeader
|
||||||
|
print_counters($ctrs, $header);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_sf_events($)
|
||||||
|
{
|
||||||
|
my $c = shift();
|
||||||
|
|
||||||
|
# Check if sampling facility is available
|
||||||
|
unless (exists $c->{cpumf}->{sf}) {
|
||||||
|
print STDERR "No CPU-measurement sampling facility detected\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
my $sf = $c->{cpumf}->{sf};
|
||||||
|
my $events = invoke_cpumf_helper("--ctr-sf");
|
||||||
|
|
||||||
|
# Remove events with missing authorization
|
||||||
|
delete $events->{0xB0000} unless exists $sf->{modes}->{basic};
|
||||||
|
delete $events->{0xBD000} unless exists $sf->{modes}->{diagnostic};
|
||||||
|
|
||||||
|
unless ($events) {
|
||||||
|
print STDERR "Sampling facility is not authorized\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display sampling facility events (aka. counters)
|
||||||
|
my $header = <<"EoHeader";
|
||||||
|
Perf events for activating the sampling facility
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Raw
|
||||||
|
event Name Description
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
EoHeader
|
||||||
|
print_counters($events, $header);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub invoke_cpumf_helper($)
|
||||||
|
{
|
||||||
|
my $parms = shift();
|
||||||
|
my $result;
|
||||||
|
|
||||||
|
# Call helper module
|
||||||
|
my $output = qx"$CPUMF_HELPER $parms";
|
||||||
|
die "Failed to run helper module for '$parms'\n" if $? >> 8;
|
||||||
|
$result = eval "$output";
|
||||||
|
die "Failed to parse helper module data\n" if $@;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
&main();
|
||||||
|
__DATA__
|
||||||
|
__END__
|
||||||
96
cpumf/data/cpum-cf-extended-z10.ctr
Normal file
96
cpumf/data/cpum-cf-extended-z10.ctr
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
Counter: 128 Name:L1I_L2_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 (L1.5) cache
|
||||||
|
.
|
||||||
|
Counter: 129 Name:L1D_L2_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the installed
|
||||||
|
cache line was sourced from the Level-2 (L1.5) cache
|
||||||
|
.
|
||||||
|
Counter: 130 Name:L1I_L3_LOCAL_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the installed
|
||||||
|
cache line was sourced from the Level-3 cache that is on the same book
|
||||||
|
as the Instruction cache (Local L2 cache)
|
||||||
|
.
|
||||||
|
Counter: 131 Name:L1D_L3_LOCAL_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the installtion
|
||||||
|
cache line was source from the Level-3 cache that is on the same book
|
||||||
|
as the Data cache (Local L2 cache)
|
||||||
|
.
|
||||||
|
Counter: 132 Name:L1I_L3_REMOTE_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the installed
|
||||||
|
cache line was sourced from a Level-3 cache that is not on the same
|
||||||
|
book as the Instruction cache (Remote L2 cache)
|
||||||
|
.
|
||||||
|
Counter: 133 Name:L1D_L3_REMOTE_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the installed
|
||||||
|
cache line was sourced from a Level-3 cache that is not on the same
|
||||||
|
book as the Data cache (Remote L2 cache)
|
||||||
|
.
|
||||||
|
Counter: 134 Name:L1D_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the installed
|
||||||
|
cache line was sourced from memory that is attached to the same book
|
||||||
|
as the Data cache (Local Memory)
|
||||||
|
.
|
||||||
|
Counter: 135 Name:L1I_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache where the installed cache
|
||||||
|
line was sourced from memory that is attached to the s ame book as the
|
||||||
|
Instruction cahe (local Memory)
|
||||||
|
.
|
||||||
|
Counter: 136 Name:L1D_RO_EXCL_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache where the line was originally
|
||||||
|
in a Read-Only state in the cache but has been updated to be in the
|
||||||
|
Exclusive state that allows stores to the cache line
|
||||||
|
.
|
||||||
|
Counter: 137 Name:L1I_CACHELINE_INVALIDATES
|
||||||
|
Description:
|
||||||
|
A cache line in the Level-1 I-Cache has been invalidated by a store on
|
||||||
|
the same CPU as the Level-1 I-Cache
|
||||||
|
.
|
||||||
|
Counter: 138 Name:ITLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written into the Level-1 Instruction
|
||||||
|
Translation Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter: 139 Name:DTLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter: 140 Name:TLB2_PTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Page Table
|
||||||
|
Entry arrays
|
||||||
|
.
|
||||||
|
Counter: 141 Name:TLB2_CRSTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays
|
||||||
|
.
|
||||||
|
Counter: 142 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays for a one-megabyte large page translation
|
||||||
|
.
|
||||||
|
Counter: 145 Name:ITLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||||
|
cycle an ITLB1 miss is in progress
|
||||||
|
.
|
||||||
|
Counter: 146 Name:DTLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||||
|
an DTLB1 miss is in progress
|
||||||
|
.
|
||||||
|
Counter: 147 Name:L2C_STORES_SENT
|
||||||
|
Description:
|
||||||
|
Incremented by one for every store sent to Level-2 (L1.5) cache
|
||||||
|
.
|
||||||
316
cpumf/data/cpum-cf-extended-z13.ctr
Normal file
316
cpumf/data/cpum-cf-extended-z13.ctr
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
# Counter decriptions for the
|
||||||
|
# IBM z13 extended counter and MT-diagnostic counter set
|
||||||
|
#
|
||||||
|
# Notes for transactional-execution mode symbolic names:
|
||||||
|
# TX .. transactional-execution mode
|
||||||
|
# NC .. nonconstrained
|
||||||
|
# C .. constrained
|
||||||
|
#
|
||||||
|
# Undefined counters in the extended counter set:
|
||||||
|
# 142
|
||||||
|
# 180-217
|
||||||
|
# 221-225
|
||||||
|
# Undefined counters in the MT-diagnostic counter set:
|
||||||
|
# 450-495
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Extended Counter Set
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
Counter:128 Name:L1D_WRITES_RO_EXCL
|
||||||
|
A directory write to the Level-1 Data cache where the line was
|
||||||
|
originally in a Read-Only state in the cache but has been updated
|
||||||
|
to be in the Exclusive state that allows stores to the cache line.
|
||||||
|
.
|
||||||
|
Counter:129 Name:DTLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter:130 Name:DTLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||||
|
a DTLB1 miss is in progress.
|
||||||
|
.
|
||||||
|
Counter:131 Name:DTLB1_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer for a one-megabyte page
|
||||||
|
.
|
||||||
|
Counter:132 Name:DTLB1_GPAGE_WRITES
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer for a two-gigabyte page.
|
||||||
|
.
|
||||||
|
Counter:133 Name:L1D_L2D_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 Data cache
|
||||||
|
.
|
||||||
|
Counter:134 Name:ITLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Instruction
|
||||||
|
Translation Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter:135 Name:ITLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||||
|
cycle an ITLB1 miss is in progress
|
||||||
|
.
|
||||||
|
Counter:136 Name:L1I_L2I_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from the Level-2 Instruction cache
|
||||||
|
.
|
||||||
|
Counter:137 Name:TLB2_PTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Page Table
|
||||||
|
Entry arrays
|
||||||
|
.
|
||||||
|
Counter:138 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Combined Region
|
||||||
|
Segment Table Entry arrays for a one-megabyte large page translation
|
||||||
|
.
|
||||||
|
Counter:139 Name:TLB2_CRSTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Combined Region
|
||||||
|
Segment Table Entry arrays
|
||||||
|
.
|
||||||
|
Counter:140 Name:TX_C_TEND
|
||||||
|
Description:
|
||||||
|
A TEND instruction has completed in a constrained transactional-execution
|
||||||
|
mode
|
||||||
|
.
|
||||||
|
Counter:141 Name:TX_NC_TEND
|
||||||
|
Description:
|
||||||
|
A TEND instruction has completed in a non-constrained
|
||||||
|
transactional-execution mode
|
||||||
|
.
|
||||||
|
Counter:143 Name:L1C_TLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Increments by one for any cycle where a Level-1 cache or Level-1 TLB miss
|
||||||
|
is in progress.
|
||||||
|
.
|
||||||
|
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Chip Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:145 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Chip Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:146 Name:L1D_ONNODE_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Node Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:147 Name:L1D_ONNODE_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Node Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:148 Name:L1D_ONNODE_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Node Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:149 Name:L1D_ONDRAWER_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Drawer Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:150 Name:L1D_ONDRAWER_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Drawer Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:151 Name:L1D_ONDRAWER_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On-Drawer Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:152 Name:L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Same-Column Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:153 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Same-Column Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:154 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Same-Column Level-3 cache
|
||||||
|
without intervention
|
||||||
|
.
|
||||||
|
Counter:155 Name:L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Far-Column Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:156 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Far-Column Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:157 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off-Drawer Far-Column Level-3 cache
|
||||||
|
without intervention
|
||||||
|
.
|
||||||
|
Counter:158 Name:L1D_ONNODE_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from On-Node memory
|
||||||
|
.
|
||||||
|
Counter:159 Name:L1D_ONDRAWER_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from On-Drawer memory
|
||||||
|
.
|
||||||
|
Counter:160 Name:L1D_OFFDRAWER_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from On-Drawer memory
|
||||||
|
.
|
||||||
|
Counter:161 Name:L1D_ONCHIP_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from On-Chip memory
|
||||||
|
.
|
||||||
|
Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On-Chip Level-3 cache without
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:163 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On Chip Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:164 Name:L1I_ONNODE_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On-Node Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:165 Name:L1I_ONNODE_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an On-Node Level-3 cache
|
||||||
|
with intervention
|
||||||
|
.
|
||||||
|
Counter:166 Name:L1I_ONNODE_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an On-Node Level-3 cache
|
||||||
|
without intervention
|
||||||
|
.
|
||||||
|
Counter:167 Name:L1I_ONDRAWER_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an On-Drawer Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:168 Name:L1I_ONDRAWER_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an On-Drawer Level-3 cache
|
||||||
|
with intervention
|
||||||
|
.
|
||||||
|
Counter:169 Name:L1I_ONDRAWER_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an On-Drawer Level-3 cache
|
||||||
|
without intervention
|
||||||
|
.
|
||||||
|
Counter:170 Name:L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||||
|
Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:171 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||||
|
Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:172 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Same-Column
|
||||||
|
Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:173 Name:L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||||
|
Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:174 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||||
|
Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:175 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from an Off-Drawer Far-Column
|
||||||
|
Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:176 Name:L1I_ONNODE_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from On-Node memory
|
||||||
|
.
|
||||||
|
Counter:177 Name:L1I_ONDRAWER_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from On-Drawer memory
|
||||||
|
.
|
||||||
|
Counter:178 Name:L1I_OFFDRAWER_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from On-Drawer memory
|
||||||
|
.
|
||||||
|
Counter:179 Name:L1I_ONCHIP_MEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where
|
||||||
|
the returned cache line was sourced from On-Chip memory
|
||||||
|
.
|
||||||
|
Counter:218 Name:TX_NC_TABORT
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a non-constrained
|
||||||
|
transactional-execution mode
|
||||||
|
.
|
||||||
|
Counter:219 Name:TX_C_TABORT_NO_SPECIAL
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a constrained transactional-execution
|
||||||
|
mode and the CPU is not using any special logic to allow the transaction
|
||||||
|
to complete
|
||||||
|
.
|
||||||
|
Counter:220 Name:TX_C_TABORT_SPECIAL
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a constrained transactional-execution
|
||||||
|
mode and the CPU is using special logic to allow the transaction to
|
||||||
|
complete
|
||||||
|
.
|
||||||
|
#
|
||||||
|
# MT-diagnostic counter set
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE
|
||||||
|
Description:
|
||||||
|
Cycle count with one thread active
|
||||||
|
.
|
||||||
|
Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE
|
||||||
|
Description:
|
||||||
|
Cycle count with two threads active
|
||||||
|
.
|
||||||
122
cpumf/data/cpum-cf-extended-z196.ctr
Normal file
122
cpumf/data/cpum-cf-extended-z196.ctr
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
Counter: 128 Name:L1D_L2_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 cache
|
||||||
|
.
|
||||||
|
Counter: 129 Name:L1I_L2_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 cache
|
||||||
|
.
|
||||||
|
Counter: 130 Name:DTLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||||
|
a DTLB1 miss is in progress.
|
||||||
|
.
|
||||||
|
Counter: 131 Name:ITLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||||
|
cycle a ITLB1 miss is in progress.
|
||||||
|
.
|
||||||
|
Counter: 133 Name:L2C_STORES_SENT
|
||||||
|
Description:
|
||||||
|
Incremented by one for every store sent to Level-2 cache
|
||||||
|
.
|
||||||
|
Counter: 134 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-3 cache
|
||||||
|
.
|
||||||
|
Counter: 135 Name:L1D_ONBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from an On Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter: 136 Name:L1I_ONBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from an On Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter: 137 Name:L1D_RO_EXCL_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache where the line was originally
|
||||||
|
in a Read-Only state in the cache but has been updated to be in the
|
||||||
|
Exclusive state that allows stores to the cache line
|
||||||
|
.
|
||||||
|
Counter: 138 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter: 139 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter: 140 Name:DTLB1_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer for a one-megabyte page
|
||||||
|
.
|
||||||
|
Counter: 141 Name:L1D_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache where the installed cache line
|
||||||
|
was sourced from memory that is attached to the same book as the Data
|
||||||
|
cache (Local Memory)
|
||||||
|
.
|
||||||
|
Counter: 142 Name:L1I_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache where the installed cache
|
||||||
|
line was sourced from memory that is attached to the same book as the
|
||||||
|
Instruction cache (Local Memory)
|
||||||
|
.
|
||||||
|
Counter: 143 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-3 cache
|
||||||
|
.
|
||||||
|
Counter: 144 Name:DTLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter: 145 Name:ITLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Instruction
|
||||||
|
Translation Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter: 146 Name:TLB2_PTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Page Table
|
||||||
|
Entry arrays
|
||||||
|
.
|
||||||
|
Counter: 147 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays for a one-megabyte large page translation
|
||||||
|
.
|
||||||
|
Counter: 148 Name:TLB2_CRSTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays
|
||||||
|
.
|
||||||
|
Counter: 150 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from an On Chip Level-3 cache
|
||||||
|
.
|
||||||
|
Counter: 152 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||||
|
.
|
||||||
|
Counter: 153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from an On Chip Level-3 cache
|
||||||
|
.
|
||||||
|
Counter: 155 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 I-Cache directory where the returned
|
||||||
|
cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||||
|
.
|
||||||
195
cpumf/data/cpum-cf-extended-zEC12.ctr
Normal file
195
cpumf/data/cpum-cf-extended-zEC12.ctr
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
Counter: 128 Name:DTLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Data TLB miss in progress. Incremented by one for every cycle
|
||||||
|
a DTLB1 miss is in progress.
|
||||||
|
.
|
||||||
|
Counter:129 Name:ITLB1_MISSES
|
||||||
|
Description:
|
||||||
|
Level-1 Instruction TLB miss in progress. Incremented by one for every
|
||||||
|
cycle a ITLB1 miss is in progress.
|
||||||
|
.
|
||||||
|
Counter:130 Name:L1D_L2I_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 Instruction cache
|
||||||
|
.
|
||||||
|
Counter:131 Name:L1I_L2I_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from the Level-2 Instruction cache
|
||||||
|
.
|
||||||
|
Counter:132 Name:L1D_L2D_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from the Level-2 Data cache
|
||||||
|
.
|
||||||
|
Counter:133 Name:DTLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter:135 Name:L1D_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache where the installed cache line
|
||||||
|
was sourced from memory that is attached to the same book as the Data
|
||||||
|
cache (Local Memory)
|
||||||
|
.
|
||||||
|
Counter:137 Name:L1I_LMEM_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache where the installed cache
|
||||||
|
line was sourced from memory that is attached to the same book as the
|
||||||
|
Instruction cache (Local Memory)
|
||||||
|
.
|
||||||
|
Counter:138 Name:L1D_RO_EXCL_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 D-Cache where the line was originally
|
||||||
|
in a Read-Only state in the cache but has been updated to be in the
|
||||||
|
Exclusive state that allows stores to the cache line
|
||||||
|
.
|
||||||
|
Counter:139 Name:DTLB1_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Data Translation
|
||||||
|
Lookaside Buffer for a one-megabyte page
|
||||||
|
.
|
||||||
|
Counter:140 Name:ITLB1_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-1 Instruction
|
||||||
|
Translation Lookaside Buffer
|
||||||
|
.
|
||||||
|
Counter:141 Name:TLB2_PTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Page Table
|
||||||
|
Entry arrays
|
||||||
|
.
|
||||||
|
Counter:142 Name:TLB2_CRSTE_HPAGE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays for a one-megabyte large page translation
|
||||||
|
.
|
||||||
|
Counter:143 Name:TLB2_CRSTE_WRITES
|
||||||
|
Description:
|
||||||
|
A translation entry has been written to the Level-2 TLB Common Region
|
||||||
|
Segment Table Entry arrays
|
||||||
|
.
|
||||||
|
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On Chip Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:145 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off Chip/On Book Level-3 cache without
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:146 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-3 cache without intervention
|
||||||
|
.
|
||||||
|
Counter:147 Name:L1D_ONBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an On Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:148 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-4 cache
|
||||||
|
.
|
||||||
|
#
|
||||||
|
# Notes for the transactional-execution mode notations:
|
||||||
|
# TX .. transactional-execution mode
|
||||||
|
# NC .. nonconstrained
|
||||||
|
# C .. constrained
|
||||||
|
#
|
||||||
|
Counter:149 Name:TX_NC_TEND
|
||||||
|
Description:
|
||||||
|
A TEND instruction has completed in a nonconstrained
|
||||||
|
transactional-execution mode
|
||||||
|
.
|
||||||
|
Counter:150 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from a On Chip Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:151 Name:L1D_OFFCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off Chip/On Book Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
# XXX Remove SOURCED from L1D_OFFBOOK_L3_SOURCED_WRITES...
|
||||||
|
Counter:152 Name:L1D_OFFBOOK_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Data cache directory where the returned
|
||||||
|
cache line was sourced from an Off Book Level-3 cache with intervention
|
||||||
|
.
|
||||||
|
Counter:153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On Chip Level-3 cache without
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:154 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||||
|
without intervention
|
||||||
|
.
|
||||||
|
Counter:155 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an Off Book Level-3 cache without
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:156 Name:L1I_ONBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:157 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an Off Book Level-4 cache
|
||||||
|
.
|
||||||
|
Counter:158 Name:TX_C_TEND
|
||||||
|
Description:
|
||||||
|
A TEND instruction has completed in a constrained transactional-execution
|
||||||
|
mode
|
||||||
|
.
|
||||||
|
Counter:159 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an On Chip Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:160 Name:L1I_OFFCHIP_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an Off Chip/On Book Level-3 cache
|
||||||
|
with intervention
|
||||||
|
.
|
||||||
|
Counter:161 Name:L1I_OFFBOOK_L3_SOURCED_WRITES_IV
|
||||||
|
Description:
|
||||||
|
A directory write to the Level-1 Instruction cache directory where the
|
||||||
|
returned cache line was sourced from an Off Book Level-3 cache with
|
||||||
|
intervention
|
||||||
|
.
|
||||||
|
Counter:177 Name:TX_NC_TABORT
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a nonconstrained
|
||||||
|
transactional-execution mode
|
||||||
|
.
|
||||||
|
Counter:178 Name:TX_C_TABORT_NO_SPECIAL
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a constrained transactional-execution
|
||||||
|
mode and the CPU is not using any special logic to allow the transaction
|
||||||
|
to complete
|
||||||
|
.
|
||||||
|
Counter:179 Name:TX_C_TABORT_SPECIAL
|
||||||
|
Description:
|
||||||
|
A transaction abort has occurred in a constrained transactional-execution
|
||||||
|
mode and the CPU is using special logic to allow the transaction to
|
||||||
|
complete
|
||||||
|
.
|
||||||
132
cpumf/data/cpum-cf-generic.ctr
Normal file
132
cpumf/data/cpum-cf-generic.ctr
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
Counter: 0 Name:CPU_CYCLES
|
||||||
|
Description:
|
||||||
|
Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 1 Name:INSTRUCTIONS
|
||||||
|
Description:
|
||||||
|
Instruction Count
|
||||||
|
.
|
||||||
|
Counter: 2 Name:L1I_DIR_WRITES
|
||||||
|
Description:
|
||||||
|
Level-1 I-Cache Directory Write Count
|
||||||
|
.
|
||||||
|
Counter: 3 Name:L1I_PENALTY_CYCLES
|
||||||
|
Description:
|
||||||
|
Level-1 I-Cache Penalty Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 4 Name:L1D_DIR_WRITES
|
||||||
|
Description:
|
||||||
|
Level-1 D-Cache Directory Write Count
|
||||||
|
.
|
||||||
|
Counter: 5 Name:L1D_PENALTY_CYCLES
|
||||||
|
Description:
|
||||||
|
Level-1 D-Cache Penalty Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES
|
||||||
|
Description:
|
||||||
|
Problem-State Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS
|
||||||
|
Description:
|
||||||
|
Problem-State Instruction Count
|
||||||
|
.
|
||||||
|
Counter: 34 Name:PROBLEM_STATE_L1I_DIR_WRITES
|
||||||
|
Description:
|
||||||
|
Problem-State Level-1 I-Cache Directory Write Count
|
||||||
|
.
|
||||||
|
Counter: 35 Name:PROBLEM_STATE_L1I_PENALTY_CYCLES
|
||||||
|
Description:
|
||||||
|
Problem-State Level-1 I-Cache Penalty Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 36 Name:PROBLEM_STATE_L1D_DIR_WRITES
|
||||||
|
Description:
|
||||||
|
Problem-State Level-1 D-Cache Directory Write Count
|
||||||
|
.
|
||||||
|
Counter: 37 Name:PROBLEM_STATE_L1D_PENALTY_CYCLES
|
||||||
|
Description:
|
||||||
|
Problem-State Level-1 D-Cache Penalty Cycle Count
|
||||||
|
.
|
||||||
|
Counter: 64 Name:PRNG_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of the PRNG functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 65 Name:PRNG_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||||
|
performing PRNG functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 66 Name:PRNG_BLOCKED_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of the PRNG functions that are issued by the CPU and are
|
||||||
|
blocked because the DEA/AES coprocessor is busy performing a function
|
||||||
|
issued by another CPU
|
||||||
|
.
|
||||||
|
Counter: 67 Name:PRNG_BLOCKED_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles blocked for the PRNG functions issued by
|
||||||
|
the CPU because the DEA/AES coprocessor is busy performing a function
|
||||||
|
issued by another CPU
|
||||||
|
.
|
||||||
|
Counter: 68 Name:SHA_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of SHA functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 69 Name:SHA_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles when the SHA coprocessor is busy performing
|
||||||
|
the SHA functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 70 Name:SHA_BLOCKED_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of the SHA functions that are issued by the CPU and are
|
||||||
|
blocked because the SHA coprocessor is busy performing a function issued
|
||||||
|
by another CPU
|
||||||
|
.
|
||||||
|
Counter: 71 Name:SHA_BLOCKED_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles blocked for the SHA functions issued by the
|
||||||
|
CPU because the SHA coprocessor is busy performing a function issued
|
||||||
|
by another CPU
|
||||||
|
.
|
||||||
|
Counter: 72 Name:DEA_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of the DEA functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 73 Name:DEA_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||||
|
performing the DEA functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 74 Name:DEA_BLOCKED_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of the DEA functions that are issued by the CPU and are
|
||||||
|
blocked because the DEA/AES coprocessor is busy performing a function
|
||||||
|
issued by another CPU
|
||||||
|
.
|
||||||
|
Counter: 75 Name:DEA_BLOCKED_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles blocked for the DEA functions issued by the
|
||||||
|
CPU because the DEA/AES coprocessor is busy performing a function issued
|
||||||
|
by another CPU
|
||||||
|
.
|
||||||
|
Counter: 76 Name:AES_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of AES functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 77 Name:AES_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles when the DEA/AES coprocessor is busy
|
||||||
|
performing the AES functions issued by the CPU
|
||||||
|
.
|
||||||
|
Counter: 78 Name:AES_BLOCKED_FUNCTIONS
|
||||||
|
Description:
|
||||||
|
Total number of AES functions that are issued by the CPU and are blocked
|
||||||
|
because the DEA/AES coprocessor is busy performing a function issued
|
||||||
|
by another CPU
|
||||||
|
.
|
||||||
|
Counter: 79 Name:AES_BLOCKED_CYCLES
|
||||||
|
Description:
|
||||||
|
Total number of CPU cycles blocked for the AES functions issued by the
|
||||||
|
CPU because the DEA/AES coprocessor is busy performing a function issued
|
||||||
|
by another CPU
|
||||||
|
.
|
||||||
17
cpumf/data/cpum-cf-hw-counter.map
Normal file
17
cpumf/data/cpum-cf-hw-counter.map
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# CPU-measurement facilities
|
||||||
|
#
|
||||||
|
# Mapping of IBM System z hardware types to extended counter set defintions
|
||||||
|
#
|
||||||
|
#
|
||||||
|
{
|
||||||
|
# Definition # File name
|
||||||
|
0 => 'cpum-cf-generic.ctr',
|
||||||
|
2097 => 'cpum-cf-extended-z10.ctr',
|
||||||
|
2098 => 'cpum-cf-extended-z10.ctr',
|
||||||
|
2817 => 'cpum-cf-extended-z196.ctr',
|
||||||
|
2818 => 'cpum-cf-extended-z196.ctr',
|
||||||
|
2827 => 'cpum-cf-extended-zEC12.ctr',
|
||||||
|
2828 => 'cpum-cf-extended-zEC12.ctr',
|
||||||
|
2964 => 'cpum-cf-extended-z13.ctr',
|
||||||
|
2965 => 'cpum-cf-extended-z13.ctr',
|
||||||
|
};
|
||||||
13
cpumf/data/cpum-sf-modes.ctr
Normal file
13
cpumf/data/cpum-sf-modes.ctr
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Perf raw event counter definitions for the
|
||||||
|
# CPU-measurment sampling facility
|
||||||
|
#
|
||||||
|
# Basic-sampling mode
|
||||||
|
Counter: 0xB0000 Name:SF_CYCLES_BASIC
|
||||||
|
Description:
|
||||||
|
Sample CPU cycles using basic-sampling mode
|
||||||
|
.
|
||||||
|
# Diagnostic-sampling mode (includes basic-sampling)
|
||||||
|
Counter: 0xBD000 Name:SF_CYCLES_BASIC_DIAG
|
||||||
|
Description:
|
||||||
|
Sample CPU cycle using diagnostic-sampling mode (not for ordinary use)
|
||||||
|
.
|
||||||
66
cpumf/man/chcpumf.8
Normal file
66
cpumf/man/chcpumf.8
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
.\" chcpumf.8
|
||||||
|
.\"
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2014, 2017
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\" ----------------------------------------------------------------------
|
||||||
|
.TH chcpumf "8" "February 2014" "s390-tools" "CPU-MF management programs"
|
||||||
|
.
|
||||||
|
.ds c \fBchcpumf\fP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
chcpumf \- manage the CPU-measurement facilities support
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B chcpumf
|
||||||
|
.RB [ \-m | \-\-min
|
||||||
|
.IR num_sdb ]
|
||||||
|
.RB [ \-x | \-\-max
|
||||||
|
.IR num_sdb ]
|
||||||
|
.br
|
||||||
|
.B chcpumf
|
||||||
|
.BR \-h | \-\-help
|
||||||
|
.br
|
||||||
|
.B chcpumf
|
||||||
|
.BR \-v | \-\-version
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The chcpumf program helps you to manage settings for the CPU-measurement
|
||||||
|
facilities for Linux on System z.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR \-m ", " \-\-min " \fInum_sdb\fP"
|
||||||
|
Specifies the minimum sampling facility buffer size in sample-data-blocks
|
||||||
|
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||||
|
|
||||||
|
This is the initial buffer size when you start the sampling facility.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-x ", " \-\-max " \fInum_sdb\fP"
|
||||||
|
Specifies the maximum sampling facility buffer size in sample-data-blocks
|
||||||
|
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||||
|
|
||||||
|
This is the maximum size to which the buffer is dynamically adjusted during
|
||||||
|
sampling.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-V ", " \-\-verbose
|
||||||
|
Displays verbose messages.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-h ", " \-\-help
|
||||||
|
Displays help information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-v ", " \-\-version
|
||||||
|
Displays version information, then exits.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR lscpumf (1)
|
||||||
70
cpumf/man/lscpumf.1
Normal file
70
cpumf/man/lscpumf.1
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
\" lscpumf.1
|
||||||
|
.\"
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2014, 2017
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\" ----------------------------------------------------------------------
|
||||||
|
.TH lscpumf "1" "February 2014" "s390-tools" "CPU-MF management programs"
|
||||||
|
.
|
||||||
|
.ds c \fBlscpumf\fP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
lscpumf \- display information about CPU-measurement facilities
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B lscpumf
|
||||||
|
.RB [ \-i | \-\-info ]
|
||||||
|
.br
|
||||||
|
.B lscpumf
|
||||||
|
.RB [ \-c | \-\-list\-counters ]
|
||||||
|
.RB [ \-C | \-\-list\-all\-counters ]
|
||||||
|
.RB [ \-s | \-\-list\-sampling\-events ]
|
||||||
|
.br
|
||||||
|
.B lscpumf
|
||||||
|
.BR \-h | \-\-help
|
||||||
|
.br
|
||||||
|
.B lscpumf
|
||||||
|
.BR \-v | \-\-version
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The lscpumf program lists information about CPU-measurement facilities that
|
||||||
|
are supported by Linux on System z.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR \-i ", " \-\-info
|
||||||
|
Displays detailed information about available and supported CPU-measurement
|
||||||
|
facilities.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-c ", " \-\-list\-counters
|
||||||
|
Lists counters that are provided by the CPU-measurement facility, omitting
|
||||||
|
counters for which the LPAR is not authorized. For counter measurements with
|
||||||
|
the perf program, the raw event identifier is displayed.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-C ", " \-\-list\-all\-counters
|
||||||
|
Lists all counters that are provided by the CPU-measurement counter facility,
|
||||||
|
regardless of LPAR authorization. To list only those counters for which the
|
||||||
|
LPAR is authorized, use the -c option.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-s ", " \-\-list\-sampling\-events
|
||||||
|
Lists perf raw events that activate the sampling facility.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-h ", " \-\-help
|
||||||
|
Displays help information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-v ", " \-\-version
|
||||||
|
Displays version information, then exits.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR chcpumf (8)
|
||||||
23
cpuplugd/Makefile
Normal file
23
cpuplugd/Makefile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
all: cpuplugd
|
||||||
|
|
||||||
|
LDLIBS += -lm
|
||||||
|
|
||||||
|
OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o
|
||||||
|
|
||||||
|
cpuplugd: $(OBJECTS)
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f cpuplugd $(OBJECTS)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cpuplugd \
|
||||||
|
$(DESTDIR)$(USRSBINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.conf.5 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man5
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
336
cpuplugd/config.c
Normal file
336
cpuplugd/config.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Config file parsing
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the value of a variable which parse_config() found within the
|
||||||
|
* configuration file. Use only for values valid >= 0, because -1 is returned
|
||||||
|
* in error case.
|
||||||
|
*/
|
||||||
|
static long parse_positive_value(char *ptr)
|
||||||
|
{
|
||||||
|
long value = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
return -1;
|
||||||
|
for (i = 0; i < strlen(ptr); i++) {
|
||||||
|
if (isdigit(ptr[i]) == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sscanf(ptr, "%ld", &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_var_rvalue(char *var_name)
|
||||||
|
{
|
||||||
|
char tmp_name[MAX_VARNAME + 3]; /* +3 for '\0', '=' and '\n' */
|
||||||
|
unsigned int tmp_length;
|
||||||
|
char *rvalue;
|
||||||
|
|
||||||
|
tmp_name[0] = '\n';
|
||||||
|
strncpy(&tmp_name[1], var_name, MAX_VARNAME + 1); /* +1 for '\0' */
|
||||||
|
tmp_length = strlen(tmp_name);
|
||||||
|
tmp_name[tmp_length] = '=';
|
||||||
|
tmp_name[tmp_length + 1] = '\0';
|
||||||
|
rvalue = strstr(varinfo, tmp_name);
|
||||||
|
if (rvalue == NULL)
|
||||||
|
return NULL;
|
||||||
|
rvalue += strlen(tmp_name);
|
||||||
|
return rvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_var(char *name, char *rvalue)
|
||||||
|
{
|
||||||
|
size_t offset, size;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (get_var_rvalue(name))
|
||||||
|
cpuplugd_exit("Variable defined twice: %s\n", name);
|
||||||
|
for (i = 0; i < sym_names_count; i++) {
|
||||||
|
if (strncmp(name, sym_names[i].name,
|
||||||
|
MAX(strlen(sym_names[i].name), strlen(name))) != 0)
|
||||||
|
continue;
|
||||||
|
cpuplugd_exit("Cannot use (pre-defined) variable name: %s\n",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = strlen(varinfo);
|
||||||
|
/* +3 because of extra '=', '\n' and '\0' */
|
||||||
|
size = offset + strlen(name) + strlen(rvalue) + 3;
|
||||||
|
if (size > varinfo_size)
|
||||||
|
//TODO realloc?
|
||||||
|
cpuplugd_exit("buffer for variables too small: need %ld, "
|
||||||
|
"have %ld (bytes)\n", size, varinfo_size);
|
||||||
|
size -= offset;
|
||||||
|
snprintf(&varinfo[offset], size, "%s=%s\n", name, rvalue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_term(char *symbol, char *name, char *rvalue, struct term **term)
|
||||||
|
{
|
||||||
|
if (!strncasecmp(name, symbol, strlen(symbol))) {
|
||||||
|
cpuplugd_debug("found the following rule: %s = %s\n",
|
||||||
|
name, rvalue);
|
||||||
|
*term = parse_term(&rvalue, OP_PRIO_NONE);
|
||||||
|
if (rvalue[0] == '\0')
|
||||||
|
return 1;
|
||||||
|
cpuplugd_exit("parsing error at %s, position: %s\n", symbol,
|
||||||
|
rvalue);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_value(char *symbol, char *name, char *rvalue, long *value)
|
||||||
|
{
|
||||||
|
if (!strncasecmp(name, symbol, strlen(symbol))) {
|
||||||
|
*value = parse_positive_value(rvalue);
|
||||||
|
cpuplugd_debug("found %s value: %ld\n", symbol, *value);
|
||||||
|
if (*value >= 0)
|
||||||
|
return 1;
|
||||||
|
cpuplugd_exit("parsing error at update\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a single line of the configuration file
|
||||||
|
*/
|
||||||
|
static void parse_configline(char *line)
|
||||||
|
{
|
||||||
|
char *match, *name, *rvalue, *start, *stop;
|
||||||
|
int i, j;
|
||||||
|
size_t len;
|
||||||
|
char temp[strlen(line) + 1];
|
||||||
|
|
||||||
|
if (line[0] == '#')
|
||||||
|
return;
|
||||||
|
for (i = j = 0; line[i] != 0; i++) /* Remove whitespace. */
|
||||||
|
if (!isblank(line[i]) && !isspace(line[i]))
|
||||||
|
temp[j++] = line[i];
|
||||||
|
temp[j] = '\0';
|
||||||
|
match = strchr(temp, '=');
|
||||||
|
if (match == NULL)
|
||||||
|
return;
|
||||||
|
*match = '\0'; /* Separate name and right hand value */
|
||||||
|
name = temp; /* left side of = */
|
||||||
|
rvalue = match + 1; /* right side of = */
|
||||||
|
/*
|
||||||
|
* remove the double quotes
|
||||||
|
* example: CPU_MIN="2"
|
||||||
|
*/
|
||||||
|
start = strchr(rvalue, '"'); /* points to first " */
|
||||||
|
stop = strrchr(rvalue, '"'); /* points to last " */
|
||||||
|
len = stop - start;
|
||||||
|
if (start != NULL && stop != NULL && len > 0) {
|
||||||
|
rvalue[len] = '\0';
|
||||||
|
rvalue = rvalue + 1;
|
||||||
|
} else
|
||||||
|
cpuplugd_exit("the configuration file has syntax "
|
||||||
|
"errors at %s, position: %s\n", name, rvalue);
|
||||||
|
|
||||||
|
if (check_term("hotplug", name, rvalue, &cfg.hotplug))
|
||||||
|
return;
|
||||||
|
if (check_term("hotunplug", name, rvalue, &cfg.hotunplug))
|
||||||
|
return;
|
||||||
|
if (check_term("memplug", name, rvalue, &cfg.memplug))
|
||||||
|
return;
|
||||||
|
if (check_term("memunplug", name, rvalue, &cfg.memunplug))
|
||||||
|
return;
|
||||||
|
if (check_term("cmm_inc", name, rvalue, &cfg.cmm_inc))
|
||||||
|
return;
|
||||||
|
if (check_term("cmm_dec", name, rvalue, &cfg.cmm_dec))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (check_value("update", name, rvalue, &cfg.update)) {
|
||||||
|
if (cfg.update > 0)
|
||||||
|
return;
|
||||||
|
cpuplugd_exit("update must be > 0\n");
|
||||||
|
}
|
||||||
|
if (check_value("cpu_min", name, rvalue, &cfg.cpu_min)) {
|
||||||
|
if (cfg.cpu_min > 0)
|
||||||
|
return;
|
||||||
|
cpuplugd_exit("cpu_min must be > 0\n");
|
||||||
|
}
|
||||||
|
if (check_value("cpu_max", name, rvalue, &cfg.cpu_max)) {
|
||||||
|
if (cfg.cpu_max == 0)
|
||||||
|
/* if cpu_max is 0, we use the overall number of cpus */
|
||||||
|
cfg.cpu_max = get_numcpus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (check_value("cmm_min", name, rvalue, &cfg.cmm_min))
|
||||||
|
return;
|
||||||
|
if (check_value("cmm_max", name, rvalue, &cfg.cmm_max))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cpuplugd_debug("found the following variable: %s = %s\n",
|
||||||
|
name, rvalue);
|
||||||
|
if (strlen(name) > MAX_VARNAME)
|
||||||
|
cpuplugd_exit("Variable name too long (max. length is "
|
||||||
|
"%i chars): %s\n", MAX_VARNAME, name);
|
||||||
|
add_var(name, rvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function used to parse the min and max values at the beginning of the
|
||||||
|
* configuration file as well as the hotplug and hotunplug rules.
|
||||||
|
*/
|
||||||
|
void parse_configfile(char *file)
|
||||||
|
{
|
||||||
|
char linebuffer[MAX_LINESIZE + 2]; /* current line incl. \n and \0 */
|
||||||
|
char *linep_offset;
|
||||||
|
FILE *filp;
|
||||||
|
|
||||||
|
filp = fopen(file, "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("Opening configuration file failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
while (fgets(linebuffer, sizeof(linebuffer), filp) != NULL) {
|
||||||
|
if (!(linep_offset = strchr(linebuffer, '\n')))
|
||||||
|
cpuplugd_exit("Line is too long (max. length is %i "
|
||||||
|
"characters): %s\n", MAX_LINESIZE,
|
||||||
|
linebuffer);
|
||||||
|
parse_configline(linebuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the required settings are found in the configuration file.
|
||||||
|
* "Autodetect" if cpu and/or memory hotplug configuration entries
|
||||||
|
* where specified
|
||||||
|
*/
|
||||||
|
void check_config()
|
||||||
|
{
|
||||||
|
int cpuid;
|
||||||
|
int lpar_status;
|
||||||
|
|
||||||
|
lpar_status = check_lpar();
|
||||||
|
if (cfg.update < 0)
|
||||||
|
cpuplugd_exit("No valid update interval specified.\n");
|
||||||
|
if (cfg.cpu_max < cfg.cpu_min && cfg.cpu_max != 0)
|
||||||
|
cpuplugd_exit("cpu_max below cpu_min, aborting.\n");
|
||||||
|
if (cfg.cpu_max < 0 || cfg.cpu_min < 0 || cfg.hotplug == NULL ||
|
||||||
|
cfg.hotunplug == NULL) {
|
||||||
|
cpuplugd_error("No valid CPU hotplug configuration "
|
||||||
|
"detected.\n");
|
||||||
|
cpu = 0;
|
||||||
|
} else {
|
||||||
|
cpu = 1;
|
||||||
|
cpuplugd_debug("Valid CPU hotplug configuration detected.\n");
|
||||||
|
}
|
||||||
|
if (cfg.cmm_max < 0 || cfg.cmm_min < 0 || cfg.memplug == NULL ||
|
||||||
|
cfg.memunplug == NULL || cfg.cmm_inc == NULL ||
|
||||||
|
cfg.cmm_max < cfg.cmm_min) {
|
||||||
|
cpuplugd_error("No valid memory hotplug configuration "
|
||||||
|
"detected.\n");
|
||||||
|
memory = 0;
|
||||||
|
} else {
|
||||||
|
memory = 1;
|
||||||
|
/*
|
||||||
|
* check if all the necessary files exit below /proc
|
||||||
|
*/
|
||||||
|
if (check_cmmfiles() != 0 && lpar_status == 0) {
|
||||||
|
cpuplugd_info("Can not open /proc/sys/vm/cmm_pages. "
|
||||||
|
"The memory hotplug function will be "
|
||||||
|
"disabled.\n");
|
||||||
|
memory = 0;
|
||||||
|
}
|
||||||
|
if (memory == 1 && lpar_status == 0)
|
||||||
|
cpuplugd_debug("Valid memory hotplug configuration "
|
||||||
|
"detected.\n");
|
||||||
|
if (memory == 1 && lpar_status == 1) {
|
||||||
|
cpuplugd_debug("Valid memory hotplug configuration "
|
||||||
|
"detected inside LPAR. "
|
||||||
|
"The memory hotplug function will be "
|
||||||
|
"disabled. \n");
|
||||||
|
memory = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (memory == 0 && cpu == 0)
|
||||||
|
cpuplugd_exit("Exiting, because neither a valid cpu nor a val"
|
||||||
|
"id memory hotplug configuration was found.\n");
|
||||||
|
/*
|
||||||
|
* Save the number of online cpus and the cmm_pagesize at startup,
|
||||||
|
* so that we can enable exactly the same amount when the daemon ends
|
||||||
|
*/
|
||||||
|
if (cpu) {
|
||||||
|
num_cpu_start = get_num_online_cpus();
|
||||||
|
cpuplugd_debug("Daemon started with %d active cpus.\n",
|
||||||
|
num_cpu_start);
|
||||||
|
/*
|
||||||
|
* Check that the initial number of cpus is not below the
|
||||||
|
* minimum
|
||||||
|
*/
|
||||||
|
if (num_cpu_start < cfg.cpu_min &&
|
||||||
|
get_numcpus() >= cfg.cpu_min) {
|
||||||
|
cpuplugd_debug("The number of online cpus is below "
|
||||||
|
"the minimum and will be increased.\n");
|
||||||
|
cpuid = 0;
|
||||||
|
while (get_num_online_cpus() < cfg.cpu_min &&
|
||||||
|
cpuid < get_numcpus()) {
|
||||||
|
if (is_online(cpuid) == 1) {
|
||||||
|
cpuid++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cpuplugd_debug("cpu with id %d is currently offline "
|
||||||
|
"and will be enabled\n", cpuid);
|
||||||
|
hotplug(cpuid);
|
||||||
|
cpuid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (get_num_online_cpus() > cfg.cpu_max) {
|
||||||
|
cpuplugd_debug("The number of online cpus is above the maximum"
|
||||||
|
" and will be decreased.\n");
|
||||||
|
cpuid = 0;
|
||||||
|
while (get_num_online_cpus() > cfg.cpu_max &&
|
||||||
|
cpuid < get_numcpus()) {
|
||||||
|
if (is_online(cpuid) != 1) {
|
||||||
|
cpuid++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cpuplugd_debug("cpu with id %d is currently online "
|
||||||
|
"and will be disabled\n", cpuid);
|
||||||
|
hotunplug(cpuid);
|
||||||
|
cpuid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cfg.cpu_min > get_numcpus())
|
||||||
|
/*
|
||||||
|
* This check only works if nobody used the
|
||||||
|
* additional_cpus in the boot parameter section
|
||||||
|
*/
|
||||||
|
cpuplugd_exit("The minimum amount of cpus is above "
|
||||||
|
"the number of available cpus.\n"
|
||||||
|
"Detected %d available cpus\n",
|
||||||
|
get_numcpus());
|
||||||
|
if (get_num_online_cpus() < cfg.cpu_min)
|
||||||
|
cpuplugd_exit("Failed to set the number of online "
|
||||||
|
"cpus to the minimum. Aborting.\n");
|
||||||
|
}
|
||||||
|
if (memory == 1) {
|
||||||
|
/*
|
||||||
|
* Check that the initial value of cmm_pages is not below
|
||||||
|
* cmm_min or above cmm_max
|
||||||
|
*/
|
||||||
|
cmm_pagesize_start = get_cmmpages_size();
|
||||||
|
if (cmm_pagesize_start < cfg.cmm_min) {
|
||||||
|
cpuplugd_debug("cmm_pages is below minimum and will "
|
||||||
|
"be increased.\n");
|
||||||
|
set_cmm_pages(cfg.cmm_min);
|
||||||
|
}
|
||||||
|
if (cmm_pagesize_start > cfg.cmm_max) {
|
||||||
|
cpuplugd_debug("cmm_pages is above the maximum and will"
|
||||||
|
" be decreased.\n");
|
||||||
|
set_cmm_pages(cfg.cmm_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
227
cpuplugd/cpu.c
Normal file
227
cpuplugd/cpu.c
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* CPU hotplug functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return overall number of available cpus. This does not necessarily
|
||||||
|
* mean that those are currently online
|
||||||
|
*/
|
||||||
|
int get_numcpus()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int number = 0;
|
||||||
|
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
/* check whether file exists and is readable */
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
|
||||||
|
if (access(path, R_OK) == 0)
|
||||||
|
number++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return number of online cpus
|
||||||
|
*/
|
||||||
|
int get_num_online_cpus()
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
int i;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int status = 0;
|
||||||
|
int value_of_onlinefile, rc;
|
||||||
|
|
||||||
|
for (i = 0; i <= get_numcpus(); i++) {
|
||||||
|
/* check wether file exists and is readable */
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
|
||||||
|
if (access(path, R_OK) != 0)
|
||||||
|
continue;
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("Cannot open cpu online file: "
|
||||||
|
"%s\n", strerror(errno));
|
||||||
|
else {
|
||||||
|
rc = fscanf(filp, "%d", &value_of_onlinefile);
|
||||||
|
if (rc != 1)
|
||||||
|
cpuplugd_exit("Cannot read cpu online file: "
|
||||||
|
"%s\n", strerror(errno));
|
||||||
|
if (value_of_onlinefile == 1)
|
||||||
|
status++;
|
||||||
|
}
|
||||||
|
fclose(filp);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable a certain cpu
|
||||||
|
*/
|
||||||
|
int hotplug(int cpuid)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int status, rc;
|
||||||
|
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
|
||||||
|
if (access(path, W_OK) == 0) {
|
||||||
|
filp = fopen(path, "w");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("Cannot open cpu online file: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
fprintf(filp, "1");
|
||||||
|
fclose(filp);
|
||||||
|
/*
|
||||||
|
* check if the attempt to enable the cpus really worked
|
||||||
|
*/
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
rc = fscanf(filp, "%d", &status);
|
||||||
|
if (rc != 1)
|
||||||
|
cpuplugd_exit("Cannot open cpu online file: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
fclose(filp);
|
||||||
|
if (status == 1) {
|
||||||
|
cpuplugd_debug("cpu with id %d enabled\n", cpuid);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
cpuplugd_debug("failed to enable cpu with id %d\n",
|
||||||
|
cpuid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cpuplugd_error("hotplugging cpu with id %d failed\n", cpuid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable a certain cpu
|
||||||
|
*/
|
||||||
|
int hotunplug(int cpuid)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
int state, rc;
|
||||||
|
int retval = -1;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
state = -1;
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
|
||||||
|
if (access(path, W_OK) == 0) {
|
||||||
|
filp = fopen(path, "w");
|
||||||
|
fprintf(filp, "0");
|
||||||
|
fclose(filp);
|
||||||
|
/*
|
||||||
|
* Check if the attempt to enable the cpus really worked
|
||||||
|
*/
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
rc = fscanf(filp, "%d", &state);
|
||||||
|
if (rc != 1)
|
||||||
|
cpuplugd_error("Failed to disable cpu with id %d\n",
|
||||||
|
cpuid);
|
||||||
|
fclose(filp);
|
||||||
|
if (state == 0)
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
cpuplugd_error("unplugging cpu with id %d failed\n", cpuid);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a certain cpu is currently online
|
||||||
|
*/
|
||||||
|
int is_online(int cpuid)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
int state;
|
||||||
|
int retval, rc;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
retval = -1;
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
|
||||||
|
if (access(path, R_OK) == 0) {
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
rc = fscanf(filp, "%d", &state);
|
||||||
|
if (rc == 1) {
|
||||||
|
if (state == 1)
|
||||||
|
retval = 1;
|
||||||
|
if (state == 0)
|
||||||
|
retval = 0;
|
||||||
|
fclose(filp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cleanup method. If the daemon is stopped, we (re)activate all cpus
|
||||||
|
*/
|
||||||
|
void reactivate_cpus()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Only enable the number of cpus which where
|
||||||
|
* available at daemon startup time
|
||||||
|
*/
|
||||||
|
int cpuid, nc;
|
||||||
|
|
||||||
|
cpuid = 0;
|
||||||
|
/* suppress verbose messages on exit */
|
||||||
|
debug = 0;
|
||||||
|
/*
|
||||||
|
* We check for num_cpu_start != 0 because we might want to
|
||||||
|
* clean up, before we queried for the number on cpus at
|
||||||
|
* startup
|
||||||
|
*/
|
||||||
|
if (num_cpu_start == 0)
|
||||||
|
return;
|
||||||
|
while (get_num_online_cpus() != num_cpu_start && cpuid < get_numcpus()) {
|
||||||
|
nc = get_num_online_cpus();
|
||||||
|
if (nc == num_cpu_start)
|
||||||
|
return;
|
||||||
|
if (nc > num_cpu_start && is_online(cpuid) == 1)
|
||||||
|
hotunplug(cpuid);
|
||||||
|
if (nc < num_cpu_start && is_online(cpuid) == 0)
|
||||||
|
hotplug(cpuid);
|
||||||
|
cpuid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In kernels > 2.6.24 cpus can be deconfigured. The following functions is used
|
||||||
|
* to check if a certain cpus is in a deconfigured state.
|
||||||
|
*/
|
||||||
|
int cpu_is_configured(int cpuid)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
int retval, state, rc;
|
||||||
|
char path[4096];
|
||||||
|
|
||||||
|
retval = -1;
|
||||||
|
sprintf(path, "/sys/devices/system/cpu/cpu%d/configure", cpuid);
|
||||||
|
if (access(path, R_OK) == 0) {
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
rc = fscanf(filp, "%d", &state);
|
||||||
|
if (rc == 1) {
|
||||||
|
if (state == 1)
|
||||||
|
retval = 1;
|
||||||
|
if (state == 0)
|
||||||
|
retval = 0;
|
||||||
|
fclose(filp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
234
cpuplugd/cpuplugd.h
Normal file
234
cpuplugd/cpuplugd.h
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Header file
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __USE_ISOC99
|
||||||
|
#define __USE_ISOC99
|
||||||
|
#endif
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#define NAME "cpuplugd"
|
||||||
|
#define MAX_HISTORY 100
|
||||||
|
#define PIDFILE "/var/run/cpuplugd.pid"
|
||||||
|
#define LOCKFILE "/var/lock/cpuplugd.lock"
|
||||||
|
#define PROCINFO_LINE 512
|
||||||
|
#define CPUSTAT_SIZE 1024
|
||||||
|
#define VARINFO_SIZE 4096
|
||||||
|
#define MAX_VARNAME 128
|
||||||
|
#define MAX_LINESIZE 2048
|
||||||
|
#define CPUSTATS 10
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Precedence of C operators
|
||||||
|
* full list:
|
||||||
|
* http://www.imada.sdu.dk/~svalle/
|
||||||
|
* courses/dm14-2005/mirror/c/_7193_tabular246.gif
|
||||||
|
*
|
||||||
|
* ()
|
||||||
|
* +-
|
||||||
|
* * /
|
||||||
|
* < >
|
||||||
|
* &
|
||||||
|
* |
|
||||||
|
*/
|
||||||
|
enum op_prio {
|
||||||
|
OP_PRIO_NONE,
|
||||||
|
OP_PRIO_OR,
|
||||||
|
OP_PRIO_AND,
|
||||||
|
/* greater and lower */
|
||||||
|
OP_PRIO_CMP,
|
||||||
|
OP_PRIO_ADD,
|
||||||
|
OP_PRIO_MULT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum operation {
|
||||||
|
/* Leaf operators */
|
||||||
|
OP_SYMBOL_LOADAVG,
|
||||||
|
OP_SYMBOL_RUNABLE,
|
||||||
|
OP_SYMBOL_CPUS,
|
||||||
|
OP_SYMBOL_USER,
|
||||||
|
OP_SYMBOL_NICE,
|
||||||
|
OP_SYMBOL_SYSTEM,
|
||||||
|
OP_SYMBOL_IDLE,
|
||||||
|
OP_SYMBOL_IOWAIT,
|
||||||
|
OP_SYMBOL_IRQ,
|
||||||
|
OP_SYMBOL_SOFTIRQ,
|
||||||
|
OP_SYMBOL_STEAL,
|
||||||
|
OP_SYMBOL_GUEST,
|
||||||
|
OP_SYMBOL_GUEST_NICE,
|
||||||
|
OP_SYMBOL_APCR,
|
||||||
|
OP_SYMBOL_SWAPRATE,
|
||||||
|
OP_SYMBOL_FREEMEM,
|
||||||
|
OP_SYMBOL_MEMINFO,
|
||||||
|
OP_SYMBOL_VMSTAT,
|
||||||
|
OP_SYMBOL_CPUSTAT,
|
||||||
|
OP_SYMBOL_TIME,
|
||||||
|
OP_CONST,
|
||||||
|
/* Unary operators */
|
||||||
|
OP_NEG,
|
||||||
|
OP_NOT,
|
||||||
|
/* Binary operators */
|
||||||
|
OP_AND,
|
||||||
|
OP_OR,
|
||||||
|
OP_GREATER,
|
||||||
|
OP_LESSER,
|
||||||
|
OP_PLUS,
|
||||||
|
OP_MINUS,
|
||||||
|
OP_MULT,
|
||||||
|
OP_DIV,
|
||||||
|
/* ... */
|
||||||
|
/*Variables which are eligible within rules*/
|
||||||
|
VAR_LOAD, /* loadaverage */
|
||||||
|
VAR_RUN, /* number of runnable processes */
|
||||||
|
VAR_ONLINE /* number of online cpus */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbols {
|
||||||
|
double loadavg;
|
||||||
|
double runnable_proc;
|
||||||
|
double onumcpus;
|
||||||
|
double idle;
|
||||||
|
double freemem;
|
||||||
|
double apcr;
|
||||||
|
double swaprate;
|
||||||
|
double user;
|
||||||
|
double nice;
|
||||||
|
double system;
|
||||||
|
double iowait;
|
||||||
|
double irq;
|
||||||
|
double softirq;
|
||||||
|
double steal;
|
||||||
|
double guest;
|
||||||
|
double guest_nice;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct term {
|
||||||
|
enum operation op;
|
||||||
|
double value;
|
||||||
|
struct term *left, *right;
|
||||||
|
char *proc_name;
|
||||||
|
unsigned int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of argurments taken fromt the configuration file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct config {
|
||||||
|
long cpu_max;
|
||||||
|
long cpu_min;
|
||||||
|
long update;
|
||||||
|
long cmm_max;
|
||||||
|
long cmm_min;
|
||||||
|
struct term *cmm_inc;
|
||||||
|
struct term *cmm_dec;
|
||||||
|
struct term *hotplug;
|
||||||
|
struct term *hotunplug;
|
||||||
|
struct term *memplug;
|
||||||
|
struct term *memunplug;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol_names {
|
||||||
|
char *name;
|
||||||
|
enum operation symop;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int foreground;
|
||||||
|
extern int debug;
|
||||||
|
extern char *configfile;
|
||||||
|
extern int debug; /* is verbose specified? */
|
||||||
|
extern int memory;
|
||||||
|
extern int cpu;
|
||||||
|
extern int num_cpu_start; /* # of online cpus at the time of the startup */
|
||||||
|
extern long cmm_pagesize_start; /* cmm_pageize at the time of daemon startup */
|
||||||
|
extern struct config cfg;
|
||||||
|
extern int reload_pending;
|
||||||
|
extern unsigned long meminfo_size;
|
||||||
|
extern unsigned long vmstat_size;
|
||||||
|
extern unsigned long cpustat_size;
|
||||||
|
extern unsigned long varinfo_size;
|
||||||
|
extern char *meminfo;
|
||||||
|
extern char *vmstat;
|
||||||
|
extern char *cpustat;
|
||||||
|
extern char *varinfo;
|
||||||
|
extern double *timestamps;
|
||||||
|
extern unsigned int history_max;
|
||||||
|
extern unsigned int history_current;
|
||||||
|
extern struct symbol_names sym_names[];
|
||||||
|
extern unsigned int sym_names_count;
|
||||||
|
|
||||||
|
int get_numcpus();
|
||||||
|
int get_num_online_cpus();
|
||||||
|
void get_loadavg_runnable(double *loadavg, double *runnable);
|
||||||
|
void clean_up();
|
||||||
|
void reactivate_cpus();
|
||||||
|
void parse_configfile(char *file);
|
||||||
|
void print_term(struct term *fn);
|
||||||
|
struct term *parse_term(char **p, enum op_prio prio);
|
||||||
|
int eval_term(struct term *fn, struct symbols *symbols);
|
||||||
|
double eval_double(struct term *fn, struct symbols *symbols);
|
||||||
|
double get_proc_value(char *procinfo, char *name, char separator);
|
||||||
|
void proc_read(char *procinfo, char *path, unsigned long size);
|
||||||
|
void proc_cpu_read(char *procinfo);
|
||||||
|
unsigned long proc_read_size(char *path);
|
||||||
|
char *get_var_rvalue(char *var_name);
|
||||||
|
void cleanup_cmm(void);
|
||||||
|
int hotplug(int cpuid);
|
||||||
|
int hotunplug(int cpuid);
|
||||||
|
int is_online(int cpuid);
|
||||||
|
long get_cmmpages_size();
|
||||||
|
void parse_options(int argc, char **argv);
|
||||||
|
void check_if_started_twice();
|
||||||
|
void store_pid(void);
|
||||||
|
void handle_signals(void);
|
||||||
|
void handle_sighup(void);
|
||||||
|
void reload_daemon(void);
|
||||||
|
int check_cmmfiles(void);
|
||||||
|
void check_config();
|
||||||
|
void set_cmm_pages(long size);
|
||||||
|
int check_lpar();
|
||||||
|
int cpu_is_configured(int cpuid);
|
||||||
|
void setup_history(void);
|
||||||
|
|
||||||
|
|
||||||
|
#define cpuplugd_info(fmt, ...) ({ \
|
||||||
|
if (foreground == 1) \
|
||||||
|
printf(fmt, ##__VA_ARGS__); \
|
||||||
|
if (foreground == 0) \
|
||||||
|
syslog(LOG_INFO, fmt, ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define cpuplugd_error(fmt, ...) ({ \
|
||||||
|
if (foreground == 1) \
|
||||||
|
fprintf(stderr, fmt, ##__VA_ARGS__); \
|
||||||
|
if (foreground == 0) \
|
||||||
|
syslog(LOG_ERR, fmt, ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define cpuplugd_debug(fmt, ...) ({ \
|
||||||
|
if (debug) \
|
||||||
|
cpuplugd_info(fmt, ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define cpuplugd_exit(fmt, ...) ({ \
|
||||||
|
cpuplugd_error(fmt, ##__VA_ARGS__); \
|
||||||
|
clean_up(); \
|
||||||
|
})
|
||||||
232
cpuplugd/daemon.c
Normal file
232
cpuplugd/daemon.c
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Daemon functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
const char *name = NAME;
|
||||||
|
static const char *pid_file = PIDFILE;
|
||||||
|
|
||||||
|
const char *const usage =
|
||||||
|
"Usage: %s [OPTIONS]\n"
|
||||||
|
"\n"
|
||||||
|
"Daemon to dynamically hotplug cpus and memory based on a set of rules\n"
|
||||||
|
"Use OPTIONS described below.\n"
|
||||||
|
"\n"
|
||||||
|
"\t-c, --config CONFIGFILE Path to the configuration file\n"
|
||||||
|
"\t-f, --foreground Run in foreground, do not detach\n"
|
||||||
|
"\t-h, --help Print this help, then exit\n"
|
||||||
|
"\t-v, --version Print version information, then exit\n"
|
||||||
|
"\t-V, --verbose Provide more verbose output\n";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print command usage
|
||||||
|
*/
|
||||||
|
void print_usage(int is_error, char program_name[])
|
||||||
|
{
|
||||||
|
fprintf(is_error ? stderr : stdout, usage, program_name);
|
||||||
|
exit(is_error ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print command version
|
||||||
|
*/
|
||||||
|
void print_version()
|
||||||
|
{
|
||||||
|
printf("%s: Linux on System z CPU hotplug daemon version %s\n",
|
||||||
|
name, RELEASE_STRING);
|
||||||
|
printf("Copyright IBM Corp. 2007, 2017\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store daemon's pid so it can be stopped
|
||||||
|
*/
|
||||||
|
void store_pid(void)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
|
||||||
|
filp = fopen(pid_file, "w");
|
||||||
|
if (!filp) {
|
||||||
|
cpuplugd_error("cannot open pid file %s: %s\n", pid_file,
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fprintf(filp, "%d\n", getpid());
|
||||||
|
fclose(filp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that we don't try to start this daemon twice
|
||||||
|
*/
|
||||||
|
void check_if_started_twice()
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
int pid, rc;
|
||||||
|
|
||||||
|
filp = fopen(pid_file, "r");
|
||||||
|
if (filp) {
|
||||||
|
rc = fscanf(filp, "%d", &pid);
|
||||||
|
if (rc != 1) {
|
||||||
|
cpuplugd_error("Reading pid file failed. Aborting!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
cpuplugd_error("pid file %s still exists.\nThis might indicate "
|
||||||
|
"that an instance of this daemon is already "
|
||||||
|
"running.\n", pid_file);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up method
|
||||||
|
*/
|
||||||
|
void clean_up()
|
||||||
|
{
|
||||||
|
cpuplugd_info("terminated\n");
|
||||||
|
remove(pid_file);
|
||||||
|
remove(LOCKFILE);
|
||||||
|
reactivate_cpus();
|
||||||
|
if (memory)
|
||||||
|
cleanup_cmm();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End the deamon
|
||||||
|
*/
|
||||||
|
void kill_daemon(int UNUSED(a))
|
||||||
|
{
|
||||||
|
cpuplugd_info("shutting down\n");
|
||||||
|
remove(pid_file);
|
||||||
|
remove(LOCKFILE);
|
||||||
|
reactivate_cpus();
|
||||||
|
if (memory)
|
||||||
|
cleanup_cmm();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reload the daemon (for lsb compliance)
|
||||||
|
*/
|
||||||
|
void reload_handler(int UNUSED(a))
|
||||||
|
{
|
||||||
|
reload_pending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload_daemon()
|
||||||
|
{
|
||||||
|
unsigned int temp_history;
|
||||||
|
long temp_mem;
|
||||||
|
int temp_cpu;
|
||||||
|
|
||||||
|
cpuplugd_info("cpuplugd restarted\n");
|
||||||
|
/*
|
||||||
|
* Before we parse the configuration file again we have to save
|
||||||
|
* the original values prior to startup. If we don't do this cpuplugd
|
||||||
|
* will no longer know how many cpus the system had before the daemon
|
||||||
|
* was started and therefor can't restore theres in case it is stopped
|
||||||
|
*/
|
||||||
|
temp_cpu = num_cpu_start;
|
||||||
|
temp_mem = cmm_pagesize_start;
|
||||||
|
temp_history = history_max;
|
||||||
|
|
||||||
|
/* clear varinfo before re-reading variables from config file */
|
||||||
|
memset(varinfo, 0, varinfo_size);
|
||||||
|
history_max = 1;
|
||||||
|
parse_configfile(configfile);
|
||||||
|
if (history_max > MAX_HISTORY)
|
||||||
|
cpuplugd_exit("History depth %i exceeded maximum (%i)\n",
|
||||||
|
history_max, MAX_HISTORY);
|
||||||
|
if (history_max != temp_history) {
|
||||||
|
free(meminfo);
|
||||||
|
free(vmstat);
|
||||||
|
free(cpustat);
|
||||||
|
free(timestamps);
|
||||||
|
setup_history();
|
||||||
|
}
|
||||||
|
check_config();
|
||||||
|
|
||||||
|
num_cpu_start = temp_cpu;
|
||||||
|
cmm_pagesize_start = temp_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up for handling SIGTERM or SIGINT
|
||||||
|
*/
|
||||||
|
void handle_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
|
||||||
|
act.sa_flags = 0;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_handler = kill_daemon;
|
||||||
|
if (sigaction(SIGTERM, &act, NULL) < 0) {
|
||||||
|
cpuplugd_error("sigaction( SIGTERM, ... ) failed - reason %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (sigaction(SIGINT, &act, NULL) < 0) {
|
||||||
|
cpuplugd_error("sigaction( SIGINT, ... ) failed - reason %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signal handler for sighup. This is used to force the deamon to reload its
|
||||||
|
* configuration file.
|
||||||
|
* This feature is also required by a lsb compliant init script
|
||||||
|
*/
|
||||||
|
void handle_sighup(void)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
|
||||||
|
act.sa_flags = 0;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_handler = reload_handler;
|
||||||
|
if (sigaction(SIGHUP, &act, NULL) < 0) {
|
||||||
|
cpuplugd_error("sigaction( SIGHUP, ... ) failed - reason %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we are running in an LPAR environment.
|
||||||
|
* This functions return 1 if we run inside an lpar and 0 otherwise
|
||||||
|
*/
|
||||||
|
int check_lpar()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
FILE *filp;
|
||||||
|
size_t bytes_read;
|
||||||
|
char buffer[2048];
|
||||||
|
char *contains_vm;
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
filp = fopen("/proc/cpuinfo", "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("cannot open /proc/cpuinfo: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp);
|
||||||
|
if (bytes_read == 0)
|
||||||
|
cpuplugd_exit("Reading /proc/cpuinfo failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
/* NUL-terminate the text */
|
||||||
|
buffer[bytes_read] = '\0';
|
||||||
|
contains_vm = strstr(buffer, "version = FF");
|
||||||
|
if (contains_vm == NULL) {
|
||||||
|
rc = 1;
|
||||||
|
cpuplugd_debug("Detected System running in LPAR mode\n");
|
||||||
|
} else
|
||||||
|
cpuplugd_debug("Detected System running in z/VM mode\n");
|
||||||
|
fclose(filp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
100
cpuplugd/getopt.c
Normal file
100
cpuplugd/getopt.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Command line parsing
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
void print_usage(int is_error, char program_name[]);
|
||||||
|
void print_version();
|
||||||
|
int foreground;
|
||||||
|
int debug;
|
||||||
|
char *configfile;
|
||||||
|
int cpu_idle_limit;
|
||||||
|
|
||||||
|
void parse_options(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int config_file_specified = -1;
|
||||||
|
const struct option long_options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h'},
|
||||||
|
{ "foreground", no_argument, NULL, 'f' },
|
||||||
|
{ "config", required_argument, NULL, 'c' },
|
||||||
|
{ "version", no_argument, NULL, 'v' },
|
||||||
|
{ "verbose", no_argument, NULL, 'V' },
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* dont run without any argument */
|
||||||
|
if (argc == 0 || argc == 1)
|
||||||
|
print_usage(0, argv[0]);
|
||||||
|
while (optind < argc) {
|
||||||
|
int index = -1;
|
||||||
|
struct option *opt = 0;
|
||||||
|
int result = getopt_long(argc, argv, "hfc:vVm",
|
||||||
|
long_options, &index);
|
||||||
|
if (result == -1)
|
||||||
|
break; /* end of list */
|
||||||
|
switch (result) {
|
||||||
|
case 'h':
|
||||||
|
print_usage(0, argv[0]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
foreground = 1;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
/*
|
||||||
|
* This prevents -cbla and enforces the
|
||||||
|
* user to specify -c bla
|
||||||
|
*/
|
||||||
|
if (strcmp(argv[optind-1], optarg) == 0) {
|
||||||
|
configfile = optarg;
|
||||||
|
config_file_specified = 1;
|
||||||
|
} else {
|
||||||
|
cpuplugd_error("Unrecognized option: %s\n",
|
||||||
|
optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
print_version();
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
debug = 1;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/* all parameter that do not appear in the optstring */
|
||||||
|
opt = (struct option *)&(long_options[index]);
|
||||||
|
printf("'%s' was specified.",
|
||||||
|
opt->name);
|
||||||
|
if (opt->has_arg == required_argument)
|
||||||
|
printf("Arg: <%s>", optarg);
|
||||||
|
printf("\n");
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
printf("Try '%s' --help' for more information.\n",
|
||||||
|
argv[0]);
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
/*
|
||||||
|
* We also run in this case if no argument was
|
||||||
|
* specified
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_usage(0, argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config_file_specified == -1) {
|
||||||
|
printf("You have to specify a configuration file!\n");
|
||||||
|
printf("Try '%s' --help' for more information.\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
157
cpuplugd/info.c
Normal file
157
cpuplugd/info.c
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* /proc info functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return current load average and runnable processes based on /proc/loadavg
|
||||||
|
*
|
||||||
|
* Example: 0.20 0.18 0.12 1/80 11206
|
||||||
|
*
|
||||||
|
* The first three columns measure CPU utilization of the last 1, 5,
|
||||||
|
* and 15 minute periods.
|
||||||
|
* The fourth column shows the number of currently running processes
|
||||||
|
* and the total number of processes.
|
||||||
|
* The last column displays the last process ID used.
|
||||||
|
*/
|
||||||
|
void get_loadavg_runnable(double *loadavg, double *runnable)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
double dummy;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
filp = fopen("/proc/loadavg", "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("cannot open kernel loadaverage "
|
||||||
|
"statistics: %s\n", strerror(errno));
|
||||||
|
rc = fscanf(filp, "%lf %lf %lf %lf/", loadavg, &dummy, &dummy,
|
||||||
|
runnable);
|
||||||
|
if (rc != 4)
|
||||||
|
cpuplugd_exit("cannot parse kernel loadaverage "
|
||||||
|
"statistics: %s\n", strerror(errno));
|
||||||
|
fclose(filp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc_cpu_read(char *procinfo)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
unsigned int rc, onumcpus;
|
||||||
|
unsigned long user, nice, system, idle, iowait, irq, softirq, steal,
|
||||||
|
guest, guest_nice, total_ticks;
|
||||||
|
double loadavg, runnable;
|
||||||
|
|
||||||
|
guest = guest_nice = 0; /* set to 0 if not present in kernel */
|
||||||
|
filp = fopen("/proc/stat", "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("/proc/stat open failed: %s\n", strerror(errno));
|
||||||
|
rc = fscanf(filp, "cpu %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", &user,
|
||||||
|
&nice, &system, &idle, &iowait, &irq, &softirq, &steal,
|
||||||
|
&guest, &guest_nice);
|
||||||
|
|
||||||
|
get_loadavg_runnable(&loadavg, &runnable);
|
||||||
|
onumcpus = get_num_online_cpus();
|
||||||
|
total_ticks = user + nice + system + idle + iowait + irq + softirq +
|
||||||
|
steal + guest + guest_nice;
|
||||||
|
|
||||||
|
rc = snprintf(procinfo, cpustat_size, "onumcpus %d\nloadavg %f\n"
|
||||||
|
"runnable_proc %f\nuser %ld\nnice %ld\nsystem %ld\n"
|
||||||
|
"idle %ld\niowait %ld\nirq %ld\nsoftirq %ld\nsteal %ld\n"
|
||||||
|
"guest %ld\nguest_nice %ld\ntotal_ticks %ld\n",
|
||||||
|
onumcpus, loadavg, runnable, user, nice, system, idle,
|
||||||
|
iowait, irq, softirq, steal, guest, guest_nice,
|
||||||
|
total_ticks);
|
||||||
|
if (rc >= cpustat_size)
|
||||||
|
cpuplugd_exit("cpustat buffer too small: need %d, have %ld "
|
||||||
|
"(bytes)\n", rc, cpustat_size);
|
||||||
|
fclose(filp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc_read(char *procinfo, char *path, unsigned long size)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
FILE *filp;
|
||||||
|
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("%s open failed: %s\n", path, strerror(errno));
|
||||||
|
|
||||||
|
bytes_read = fread(procinfo, 1, size, filp);
|
||||||
|
if (bytes_read == 0)
|
||||||
|
cpuplugd_exit("%s read failed\n", path);
|
||||||
|
if (bytes_read == size)
|
||||||
|
cpuplugd_exit("procinfo buffer too small for %s\n", path);
|
||||||
|
|
||||||
|
procinfo[bytes_read] = '\0';
|
||||||
|
fclose(filp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long proc_read_size(char *path)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
char buf[PROCINFO_LINE];
|
||||||
|
char *linep, *linep_offset;
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
filp = fopen(path, "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("%s open failed: %s\n", path, strerror(errno));
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
while ((linep = fgets(buf, sizeof(buf), filp))) {
|
||||||
|
if (!(linep_offset = strchr(linep, '\n')))
|
||||||
|
cpuplugd_exit("buf too small for line\n");
|
||||||
|
size = size + linep_offset - linep + 1;
|
||||||
|
}
|
||||||
|
fclose(filp);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_proc_value(char *procinfo, char *name, char separator)
|
||||||
|
{
|
||||||
|
char buf[PROCINFO_LINE];
|
||||||
|
char *proc_offset;
|
||||||
|
unsigned long proc_length, name_length;
|
||||||
|
double value;
|
||||||
|
int found;
|
||||||
|
|
||||||
|
value = -1;
|
||||||
|
found = 0;
|
||||||
|
name_length = strlen(name);
|
||||||
|
while ((proc_offset = strchr(procinfo, separator))) {
|
||||||
|
proc_length = proc_offset - procinfo;
|
||||||
|
/*
|
||||||
|
* proc_read_size() made sure that proc_length < PROCINFO_LINE
|
||||||
|
*/
|
||||||
|
memcpy(buf, procinfo, proc_length);
|
||||||
|
buf[proc_length] = '\0';
|
||||||
|
procinfo = proc_offset + 1;
|
||||||
|
if (strncmp(buf, name, MAX(proc_length, name_length)) == 0) {
|
||||||
|
errno = 0;
|
||||||
|
value = strtod(procinfo, NULL);
|
||||||
|
if (errno)
|
||||||
|
cpuplugd_exit("strtod failed\n");
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
proc_offset = strchr(procinfo, '\n');
|
||||||
|
procinfo = proc_offset + 1;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
cpuplugd_exit("Symbol %s not found, check your config file\n",
|
||||||
|
name);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
485
cpuplugd/main.c
Normal file
485
cpuplugd/main.c
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Main functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <fenv.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
struct symbol_names sym_names[] = {
|
||||||
|
{ "loadavg", OP_SYMBOL_LOADAVG },
|
||||||
|
{ "runnable_proc", OP_SYMBOL_RUNABLE },
|
||||||
|
{ "onumcpus", OP_SYMBOL_CPUS },
|
||||||
|
{ "user", OP_SYMBOL_USER },
|
||||||
|
{ "nice", OP_SYMBOL_NICE },
|
||||||
|
{ "system", OP_SYMBOL_SYSTEM },
|
||||||
|
{ "idle", OP_SYMBOL_IDLE },
|
||||||
|
{ "iowait", OP_SYMBOL_IOWAIT },
|
||||||
|
{ "irq", OP_SYMBOL_IRQ },
|
||||||
|
{ "softirq", OP_SYMBOL_SOFTIRQ },
|
||||||
|
{ "steal", OP_SYMBOL_STEAL },
|
||||||
|
{ "guest_nice", OP_SYMBOL_GUEST_NICE },
|
||||||
|
{ "guest", OP_SYMBOL_GUEST },
|
||||||
|
{ "swaprate", OP_SYMBOL_SWAPRATE },
|
||||||
|
{ "apcr", OP_SYMBOL_APCR },
|
||||||
|
{ "freemem", OP_SYMBOL_FREEMEM },
|
||||||
|
{ "meminfo.", OP_SYMBOL_MEMINFO },
|
||||||
|
{ "vmstat.", OP_SYMBOL_VMSTAT },
|
||||||
|
{ "cpustat.", OP_SYMBOL_CPUSTAT },
|
||||||
|
{ "time", OP_SYMBOL_TIME },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct config cfg = {
|
||||||
|
.cpu_max = -1,
|
||||||
|
.cpu_min = -1,
|
||||||
|
.update = -1,
|
||||||
|
.cmm_min = -1,
|
||||||
|
.cmm_max = -1,
|
||||||
|
.cmm_inc = NULL,
|
||||||
|
.cmm_dec = NULL,
|
||||||
|
.memplug = NULL,
|
||||||
|
.memunplug = NULL,
|
||||||
|
.hotplug = NULL,
|
||||||
|
.hotunplug = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
int num_cpu_start, memory, cpu, reload_pending;
|
||||||
|
long cmm_pagesize_start;
|
||||||
|
unsigned long meminfo_size, vmstat_size, cpustat_size, varinfo_size;
|
||||||
|
char *meminfo, *vmstat, *cpustat, *varinfo;
|
||||||
|
double *timestamps;
|
||||||
|
unsigned int history_max, history_current, history_prev, sym_names_count;
|
||||||
|
|
||||||
|
static struct symbols symbols;
|
||||||
|
static jmp_buf jmpenv;
|
||||||
|
static struct sigaction act;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle the sigfpe signal which we might catch during rule evaluating
|
||||||
|
*/
|
||||||
|
static void sigfpe_handler(int UNUSED(sig))
|
||||||
|
{
|
||||||
|
longjmp(jmpenv, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void eval_cpu_rules(void)
|
||||||
|
{
|
||||||
|
double diffs[CPUSTATS], diffs_total, percent_factor;
|
||||||
|
char *procinfo_current, *procinfo_prev;
|
||||||
|
int cpu, nr_cpus, on_off;
|
||||||
|
|
||||||
|
nr_cpus = get_numcpus();
|
||||||
|
procinfo_current = cpustat + history_current * cpustat_size;
|
||||||
|
procinfo_prev = cpustat + history_prev * cpustat_size;
|
||||||
|
|
||||||
|
diffs[0] = get_proc_value(procinfo_current, "user", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "user", ' ');
|
||||||
|
diffs[1] = get_proc_value(procinfo_current, "nice", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "nice", ' ');
|
||||||
|
diffs[2] = get_proc_value(procinfo_current, "system", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "system", ' ');
|
||||||
|
diffs[3] = get_proc_value(procinfo_current, "idle", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "idle", ' ');
|
||||||
|
diffs[4] = get_proc_value(procinfo_current, "iowait", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "iowait", ' ');
|
||||||
|
diffs[5] = get_proc_value(procinfo_current, "irq", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "irq", ' ');
|
||||||
|
diffs[6] = get_proc_value(procinfo_current, "softirq", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "softirq", ' ');
|
||||||
|
diffs[7] = get_proc_value(procinfo_current, "steal", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "steal", ' ');
|
||||||
|
diffs[8] = get_proc_value(procinfo_current, "guest", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "guest", ' ');
|
||||||
|
diffs[9] = get_proc_value(procinfo_current, "guest_nice", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "guest_nice", ' ');
|
||||||
|
|
||||||
|
diffs_total = get_proc_value(procinfo_current, "total_ticks", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "total_ticks", ' ');
|
||||||
|
if (diffs_total == 0)
|
||||||
|
diffs_total = 1;
|
||||||
|
|
||||||
|
symbols.loadavg = get_proc_value(procinfo_current, "loadavg", ' ');
|
||||||
|
symbols.runnable_proc = get_proc_value(procinfo_current,
|
||||||
|
"runnable_proc", ' ');
|
||||||
|
symbols.onumcpus = get_proc_value(procinfo_current, "onumcpus", ' ');
|
||||||
|
|
||||||
|
percent_factor = 100 * symbols.onumcpus;
|
||||||
|
symbols.user = (diffs[0] / diffs_total) * percent_factor;
|
||||||
|
symbols.nice = (diffs[1] / diffs_total) * percent_factor;
|
||||||
|
symbols.system = (diffs[2] / diffs_total) * percent_factor;
|
||||||
|
symbols.idle = (diffs[3] / diffs_total) * percent_factor;
|
||||||
|
symbols.iowait = (diffs[4] / diffs_total) * percent_factor;
|
||||||
|
symbols.irq = (diffs[5] / diffs_total) * percent_factor;
|
||||||
|
symbols.softirq = (diffs[6] / diffs_total) * percent_factor;
|
||||||
|
symbols.steal = (diffs[7] / diffs_total) * percent_factor;
|
||||||
|
symbols.guest = (diffs[8] / diffs_total) * percent_factor;
|
||||||
|
symbols.guest_nice = (diffs[9] / diffs_total) * percent_factor;
|
||||||
|
|
||||||
|
/* only use this for development and testing */
|
||||||
|
cpuplugd_debug("cpustat values:\n%s", cpustat + history_current *
|
||||||
|
cpustat_size);
|
||||||
|
if (debug && foreground == 1) {
|
||||||
|
printf("-------------------- CPU --------------------\n");
|
||||||
|
printf("cpu_min: %ld\n", cfg.cpu_min);
|
||||||
|
printf("cpu_max: %ld\n", cfg.cpu_max);
|
||||||
|
printf("loadavg: %f \n", symbols.loadavg);
|
||||||
|
printf("user percent = %f\n", symbols.user);
|
||||||
|
printf("nice percent = %f\n", symbols.nice);
|
||||||
|
printf("system percent = %f\n", symbols.system);
|
||||||
|
printf("idle percent = %f\n", symbols.idle);
|
||||||
|
printf("iowait percent = %f\n", symbols.iowait);
|
||||||
|
printf("irq percent = %f\n", symbols.irq);
|
||||||
|
printf("softirq percent = %f\n", symbols.softirq);
|
||||||
|
printf("steal percent = %f\n", symbols.steal);
|
||||||
|
printf("guest percent = %f\n", symbols.guest);
|
||||||
|
printf("guest_nice percent = %f\n", symbols.guest_nice);
|
||||||
|
printf("numcpus %d\n", nr_cpus);
|
||||||
|
printf("runnable_proc: %d\n", (int) symbols.runnable_proc);
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
printf("onumcpus: %d\n", (int) symbols.onumcpus);
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
printf("hotplug: ");
|
||||||
|
print_term(cfg.hotplug);
|
||||||
|
printf("\n");
|
||||||
|
printf("hotunplug: ");
|
||||||
|
print_term(cfg.hotunplug);
|
||||||
|
printf("\n");
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
on_off = 0;
|
||||||
|
/* Evaluate the hotplug rule */
|
||||||
|
if (eval_term(cfg.hotplug, &symbols))
|
||||||
|
on_off++;
|
||||||
|
/* Evaluate the hotunplug rule only if hotplug did not match */
|
||||||
|
else if (eval_term(cfg.hotunplug, &symbols))
|
||||||
|
on_off--;
|
||||||
|
if (on_off > 0) {
|
||||||
|
/* check the cpu nr limit */
|
||||||
|
if (symbols.onumcpus + 1 > cfg.cpu_max) {
|
||||||
|
/* cpu limit reached */
|
||||||
|
cpuplugd_debug("maximum cpu limit is reached\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* try to find a offline cpu */
|
||||||
|
for (cpu = 0; cpu < nr_cpus; cpu++)
|
||||||
|
if (is_online(cpu) == 0 && cpu_is_configured(cpu) != 0)
|
||||||
|
break;
|
||||||
|
if (cpu < nr_cpus) {
|
||||||
|
cpuplugd_debug("cpu with id %d is currently offline "
|
||||||
|
"and will be enabled\n", cpu);
|
||||||
|
if (hotplug(cpu) == -1)
|
||||||
|
cpuplugd_debug("unable to find a cpu which "
|
||||||
|
"can be enabled\n");
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* In case we tried to enable a cpu but this failed.
|
||||||
|
* This is the case if a cpu is deconfigured
|
||||||
|
*/
|
||||||
|
cpuplugd_debug("unable to find a cpu which can "
|
||||||
|
"be enabled\n");
|
||||||
|
}
|
||||||
|
} else if (on_off < 0) {
|
||||||
|
/* check cpu nr limit */
|
||||||
|
if (symbols.onumcpus <= cfg.cpu_min) {
|
||||||
|
cpuplugd_debug("minimum cpu limit is reached\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* try to find a online cpu */
|
||||||
|
for (cpu = get_numcpus() - 1; cpu >= 0; cpu--) {
|
||||||
|
if (is_online(cpu) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cpu > 0) {
|
||||||
|
cpuplugd_debug("cpu with id %d is currently online "
|
||||||
|
"and will be disabled\n", cpu);
|
||||||
|
hotunplug(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void eval_mem_rules(double interval)
|
||||||
|
{
|
||||||
|
long cmmpages_size, cmm_inc, cmm_dec, cmm_new;
|
||||||
|
double free_memory, swaprate, apcr;
|
||||||
|
char *procinfo_current, *procinfo_prev;
|
||||||
|
|
||||||
|
procinfo_current = meminfo + history_current * meminfo_size;
|
||||||
|
free_memory = get_proc_value(procinfo_current, "MemFree", ':');
|
||||||
|
|
||||||
|
procinfo_current = vmstat + history_current * vmstat_size;
|
||||||
|
procinfo_prev = vmstat + history_prev * vmstat_size;
|
||||||
|
swaprate = (get_proc_value(procinfo_current, "pswpin", ' ') +
|
||||||
|
get_proc_value(procinfo_current, "pswpout", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "pswpin", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "pswpout", ' ')) /
|
||||||
|
interval;
|
||||||
|
apcr = (get_proc_value(procinfo_current, "pgpgin", ' ') +
|
||||||
|
get_proc_value(procinfo_current, "pgpgout", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "pgpgin", ' ') -
|
||||||
|
get_proc_value(procinfo_prev, "pgpgout", ' ')) /
|
||||||
|
interval;
|
||||||
|
|
||||||
|
cmmpages_size = get_cmmpages_size();
|
||||||
|
symbols.apcr = apcr; // apcr in 512 byte blocks / sec
|
||||||
|
symbols.swaprate = swaprate; // swaprate in 4K pages / sec
|
||||||
|
symbols.freemem = free_memory / 1024; // freemem in MB
|
||||||
|
|
||||||
|
cmm_inc = eval_double(cfg.cmm_inc, &symbols);
|
||||||
|
/* cmm_dec is optional */
|
||||||
|
if (cfg.cmm_dec)
|
||||||
|
cmm_dec = eval_double(cfg.cmm_dec, &symbols);
|
||||||
|
else
|
||||||
|
cmm_dec = cmm_inc;
|
||||||
|
|
||||||
|
/* only use this for development and testing */
|
||||||
|
if (debug && foreground == 1) {
|
||||||
|
printf("------------------- Memory ------------------\n");
|
||||||
|
printf("cmm_min: %ld\n", cfg.cmm_min);
|
||||||
|
printf("cmm_max: %ld\n", cfg.cmm_max);
|
||||||
|
printf("swaprate: %f\n", symbols.swaprate);
|
||||||
|
printf("apcr: %f\n", symbols.apcr);
|
||||||
|
printf("cmm_inc: %ld = ", cmm_inc);
|
||||||
|
print_term(cfg.cmm_inc);
|
||||||
|
printf("\n");
|
||||||
|
printf("cmm_dec: %ld = ", cmm_dec);
|
||||||
|
if (cfg.cmm_dec)
|
||||||
|
print_term(cfg.cmm_dec);
|
||||||
|
else
|
||||||
|
print_term(cfg.cmm_inc);
|
||||||
|
printf("\n");
|
||||||
|
printf("free memory: %f MB\n", symbols.freemem);
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
printf("cmm_pages: %ld\n", cmmpages_size);
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
printf("memplug: ");
|
||||||
|
print_term(cfg.memplug);
|
||||||
|
printf("\n");
|
||||||
|
printf("memunplug: ");
|
||||||
|
print_term(cfg.memunplug);
|
||||||
|
printf("\n");
|
||||||
|
printf("---------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmm_new = cmmpages_size;
|
||||||
|
/* Evaluate the memplug rule */
|
||||||
|
if (eval_term(cfg.memplug, &symbols)) {
|
||||||
|
if (cmm_dec < 0) {
|
||||||
|
cpuplugd_error("cmm_dec went negative (%ld), set it "
|
||||||
|
"to 0.\n", cmm_dec);
|
||||||
|
cmm_dec = 0;
|
||||||
|
}
|
||||||
|
cmm_new -= cmm_dec;
|
||||||
|
/* Evaluate the memunplug rule only if memplug did not match */
|
||||||
|
} else if (eval_term(cfg.memunplug, &symbols)) {
|
||||||
|
if (cmm_inc < 0) {
|
||||||
|
cpuplugd_error("cmm_inc went negative (%ld), set it "
|
||||||
|
"to 0.\n", cmm_inc);
|
||||||
|
cmm_inc = 0;
|
||||||
|
}
|
||||||
|
cmm_new += cmm_inc;
|
||||||
|
}
|
||||||
|
if (cmm_new < cfg.cmm_min) {
|
||||||
|
cpuplugd_debug("minimum memory limit is reached\n");
|
||||||
|
cmm_new = cfg.cmm_min;
|
||||||
|
}
|
||||||
|
if (cmm_new > cfg.cmm_max) {
|
||||||
|
cpuplugd_debug("maximum memory limit is reached\n");
|
||||||
|
cmm_new = cfg.cmm_max;
|
||||||
|
}
|
||||||
|
if (cmm_new != cmmpages_size)
|
||||||
|
set_cmm_pages(cmm_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void time_read(double *timestamps)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
cpuplugd_debug("\n==================== New interval "
|
||||||
|
"====================\n");
|
||||||
|
rc = gettimeofday(&tv, NULL);
|
||||||
|
if (!rc) {
|
||||||
|
*timestamps = tv.tv_sec + (double) tv.tv_usec / 1000000;
|
||||||
|
cpuplugd_debug("Timestamp: %s (%f seconds since "
|
||||||
|
"the Epoch)\n", ctime(&tv.tv_sec), *timestamps);
|
||||||
|
} else
|
||||||
|
cpuplugd_exit("gettimeofday failed: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_history()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The /proc file size will vary during intervals, use double of current
|
||||||
|
* size to have enough buffer for growing values.
|
||||||
|
*/
|
||||||
|
meminfo_size = proc_read_size("/proc/meminfo") * 2;
|
||||||
|
vmstat_size = proc_read_size("/proc/vmstat") * 2;
|
||||||
|
cpustat_size = CPUSTAT_SIZE;
|
||||||
|
|
||||||
|
meminfo = malloc(meminfo_size * (history_max + 1));
|
||||||
|
if (!meminfo)
|
||||||
|
cpuplugd_exit("Out of memory: meminfo\n");
|
||||||
|
vmstat = malloc(vmstat_size * (history_max + 1));
|
||||||
|
if (!vmstat)
|
||||||
|
cpuplugd_exit("Out of memory: vmstat\n");
|
||||||
|
cpustat = malloc(cpustat_size * (history_max + 1));
|
||||||
|
if (!cpustat)
|
||||||
|
cpuplugd_exit("Out of memory: cpustat\n");
|
||||||
|
timestamps = malloc(sizeof(double) * (history_max + 1));
|
||||||
|
if (!timestamps)
|
||||||
|
cpuplugd_exit("Out of memory: timestamps\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read history data, at least 1 interval for swaprate, apcr, idle, etc.
|
||||||
|
*/
|
||||||
|
history_current = 0;
|
||||||
|
cpuplugd_info("Waiting %i intervals to accumulate history.\n",
|
||||||
|
history_max);
|
||||||
|
do {
|
||||||
|
time_read(×tamps[history_current]);
|
||||||
|
proc_read(meminfo + history_current * meminfo_size,
|
||||||
|
"/proc/meminfo", meminfo_size);
|
||||||
|
proc_read(vmstat + history_current * vmstat_size,
|
||||||
|
"/proc/vmstat", vmstat_size);
|
||||||
|
proc_cpu_read(cpustat + history_current * cpustat_size);
|
||||||
|
sleep(cfg.update);
|
||||||
|
history_current++;
|
||||||
|
} while (history_current < history_max);
|
||||||
|
history_current--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
double interval;
|
||||||
|
int fd, rc;
|
||||||
|
|
||||||
|
reload_pending = 0;
|
||||||
|
sym_names_count = sizeof(sym_names) / sizeof(struct symbol_names);
|
||||||
|
varinfo_size = VARINFO_SIZE;
|
||||||
|
varinfo = calloc(varinfo_size, 1);
|
||||||
|
if (!varinfo) {
|
||||||
|
cpuplugd_error("Out of memory: varinfo\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* varinfo must start with '\n' for correct string matching
|
||||||
|
* in get_var_rvalue().
|
||||||
|
*/
|
||||||
|
varinfo[0] = '\n';
|
||||||
|
|
||||||
|
/* Parse the command line options */
|
||||||
|
parse_options(argc, argv);
|
||||||
|
|
||||||
|
/* flock() lock file to prevent multiple instances of cpuplugd */
|
||||||
|
fd = open(LOCKFILE, O_CREAT | O_RDONLY, S_IRUSR);
|
||||||
|
if (fd == -1) {
|
||||||
|
cpuplugd_error("Cannot open lock file %s: %s\n", LOCKFILE,
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
rc = flock(fd, LOCK_EX | LOCK_NB);
|
||||||
|
if (rc) {
|
||||||
|
cpuplugd_error("flock() failed on lock file %s: %s\nThis might "
|
||||||
|
"indicate that an instance of this daemon is "
|
||||||
|
"already running.\n", LOCKFILE, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure that the daemon is not started multiple times */
|
||||||
|
check_if_started_twice();
|
||||||
|
/* Store daemon pid also in foreground mode */
|
||||||
|
handle_signals();
|
||||||
|
handle_sighup();
|
||||||
|
|
||||||
|
/* Need 1 history level minimum for internal symbols */
|
||||||
|
history_max = 1;
|
||||||
|
/*
|
||||||
|
* Parse arguments from the configuration file, also calculate
|
||||||
|
* history_max
|
||||||
|
*/
|
||||||
|
parse_configfile(configfile);
|
||||||
|
if (history_max > MAX_HISTORY)
|
||||||
|
cpuplugd_exit("History depth %i exceeded maximum (%i)\n",
|
||||||
|
history_max, MAX_HISTORY);
|
||||||
|
/* Check the settings in the configuration file */
|
||||||
|
check_config();
|
||||||
|
|
||||||
|
if (!foreground) {
|
||||||
|
rc = daemon(1, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
cpuplugd_exit("Detach from terminal failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
/* Store daemon pid */
|
||||||
|
store_pid();
|
||||||
|
/* Unlock lock file */
|
||||||
|
flock(fd, LOCK_UN);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
/* Install signal handler for floating point exceptions */
|
||||||
|
rc = feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW |
|
||||||
|
FE_INVALID);
|
||||||
|
act.sa_flags = SA_NODEFER;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_handler = sigfpe_handler;
|
||||||
|
if (sigaction(SIGFPE, &act, NULL) < 0)
|
||||||
|
cpuplugd_exit("sigaction( SIGFPE, ... ) failed - reason %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
setup_history();
|
||||||
|
|
||||||
|
/* Main loop */
|
||||||
|
while (1) {
|
||||||
|
if (reload_pending) { // check for daemon reload
|
||||||
|
reload_daemon();
|
||||||
|
reload_pending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
history_prev = history_current;
|
||||||
|
history_current = (history_current + 1) % (history_max + 1);
|
||||||
|
time_read(×tamps[history_current]);
|
||||||
|
proc_read(meminfo + history_current * meminfo_size,
|
||||||
|
"/proc/meminfo", meminfo_size);
|
||||||
|
proc_read(vmstat + history_current * vmstat_size,
|
||||||
|
"/proc/vmstat", vmstat_size);
|
||||||
|
proc_cpu_read(cpustat + history_current * cpustat_size);
|
||||||
|
interval = timestamps[history_current] -
|
||||||
|
timestamps[history_prev];
|
||||||
|
cpuplugd_debug("config update interval: %ld seconds\n",
|
||||||
|
cfg.update);
|
||||||
|
cpuplugd_debug("real update interval: %f seconds\n", interval);
|
||||||
|
|
||||||
|
/* Run code that may signal failure via longjmp. */
|
||||||
|
if (cpu == 1) {
|
||||||
|
if (setjmp(jmpenv) == 0)
|
||||||
|
eval_cpu_rules();
|
||||||
|
else
|
||||||
|
cpuplugd_error("Floating point exception, "
|
||||||
|
"skipping cpu rule "
|
||||||
|
"evaluation.\n");
|
||||||
|
}
|
||||||
|
if (memory == 1) {
|
||||||
|
if (setjmp(jmpenv) == 0)
|
||||||
|
eval_mem_rules(interval);
|
||||||
|
else
|
||||||
|
cpuplugd_error("Floating point exception, "
|
||||||
|
"skipping memory rule "
|
||||||
|
"evaluation.\n");
|
||||||
|
}
|
||||||
|
sleep(cfg.update);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
64
cpuplugd/man/cpuplugd.8
Normal file
64
cpuplugd/man/cpuplugd.8
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH CPUPLUGD 8 "May 2011" "s390-tools"
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
cpuplugd \- Linux on System z CPU and memory hotplug daemon
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cpuplugd
|
||||||
|
.RI [ OPTIONS ]
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The cpuplugd daemon dynamically enables and disables CPUs and increases or
|
||||||
|
decreases the cooperative memory management (CMM) page pool based on a set of
|
||||||
|
rules.
|
||||||
|
|
||||||
|
When the daemon is stopped, the size of the CMM page pool and the number
|
||||||
|
of active CPUs are reset to the values they had before the cpuplugd was started.
|
||||||
|
|
||||||
|
This program can be used to control the number of CPUs for Linux on z/VM
|
||||||
|
and for Linux in LPAR mode. The memory hotplug feature (CMM page pool) applies
|
||||||
|
to Linux on z/VM only.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-c\fP or \fB\-\-config\fP \fI<configuration file>\fP
|
||||||
|
Specify the absolute path to the configuration file. This option is mandatory.
|
||||||
|
The default configuration file can be found in /etc/cpuplugd.conf.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-f\fP or \fB\-\-foreground\fP
|
||||||
|
Run in the foreground and not as daemon. If this option is
|
||||||
|
omitted, the program runs in the background.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-h\fP or \fB\-\-help\fP
|
||||||
|
Print usage message and exit.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fP or \fB\-\-version\fP
|
||||||
|
Print Version information and exit.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-V\fP or \fB\-\-verbose\fP
|
||||||
|
Print verbose messages to stdout (when running in foreground)
|
||||||
|
or to syslog otherwise.
|
||||||
|
This options is mainly used for debugging purposes.
|
||||||
|
.
|
||||||
|
.SH EXAMPLES
|
||||||
|
To test a setup start cpuplugd in foreground mode using verbose output:
|
||||||
|
.br
|
||||||
|
.RS 4
|
||||||
|
cpuplugd \-V \-f \-c /etc/cpuplugd.conf
|
||||||
|
.RE
|
||||||
|
|
||||||
|
For daemon mode, start cpuplugd from an init script as follows:
|
||||||
|
.br
|
||||||
|
.RS 4
|
||||||
|
cpuplugd \-c /etc/cpuplugd.conf
|
||||||
|
.RE
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR cpuplugd.conf (5)
|
||||||
284
cpuplugd/man/cpuplugd.conf.5
Normal file
284
cpuplugd/man/cpuplugd.conf.5
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH CPUPLUGD.CONF 5 "May 2011" "s390-tools"
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
cpuplugd.conf \- Configuration file for the Linux on System z CPU and memory
|
||||||
|
hotplug daemon
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The cpuplugd.conf configuration file contains the configuration information for
|
||||||
|
the Linux for System z CPU and memory hotplug daemon.
|
||||||
|
|
||||||
|
Use this file to specify rules for enabling or disabling CPUs and for adding
|
||||||
|
or removing memory.
|
||||||
|
.
|
||||||
|
.SS "CPU hotplug"
|
||||||
|
CPUs can be enabled and disabled through a sysfs interface.
|
||||||
|
The status file for a CPU, here CPU number 16 (counting starts at 0),
|
||||||
|
is /sys/devices/system/cpu/cpu15/online.
|
||||||
|
|
||||||
|
Writing a 0 to this file disables the CPU. Writing a 1 enables the CPU.
|
||||||
|
.
|
||||||
|
.SS "Memory hotplug"
|
||||||
|
The rules that add or remove memory use the cooperative memory management
|
||||||
|
(CMM) feature.
|
||||||
|
|
||||||
|
CMM is a mechanism to reduce the memory available to Linux instances that run
|
||||||
|
as guests of z/VM.
|
||||||
|
CMM allocates pages to a dynamic page pool not available to Linux.
|
||||||
|
A diagnose code indicates to z/VM that the pages in the page pool are out of
|
||||||
|
use. z/VM can then immediately reuse these pages for other guests.
|
||||||
|
.
|
||||||
|
.SS "Layout of the configuration file"
|
||||||
|
The configuration file contains variables specifying static numbers or
|
||||||
|
expressions. They are of the format \fB<variable>="<value>"\fP and they
|
||||||
|
need to be specified within one line. Expressions can be specified
|
||||||
|
to calculate algebraic values or to define boolean rules, which determine
|
||||||
|
when a hotplug/hotunplug action should be taken. The maximum valid line
|
||||||
|
length is 2048 characters.
|
||||||
|
|
||||||
|
There are case-insensitive pre-defined and case-sensitive user-defined
|
||||||
|
variables. The configuration file must include specifications for all
|
||||||
|
pre-defined variables. If a variable is not set, the hotplug function it
|
||||||
|
applies to, CPU or memory, is disabled.
|
||||||
|
The only exception to this rule is CMM_DEC, which defaults to the setting
|
||||||
|
for CMM_INC if omitted. If a pre-defined variable is set more than once, only
|
||||||
|
the last occurrence is used. User-defined variables must not be set more than
|
||||||
|
once.
|
||||||
|
.
|
||||||
|
.SS "Hotplug rules"
|
||||||
|
Set these pre-defined variables to an expression that resolves to a boolean
|
||||||
|
value (true or false). These variables trigger hotplug actions. Setting a
|
||||||
|
variable to "0" disables the action.
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBHOTPLUG\fP - used to enable CPUs
|
||||||
|
.IP "-" 2
|
||||||
|
\fBHOTUNPLUG\fP - used to disable CPUs
|
||||||
|
.IP "-" 2
|
||||||
|
\fBMEMPLUG\fP - used to increase the available memory
|
||||||
|
.IP "-" 2
|
||||||
|
\fBMEMUNPLUG\fP - used to decrease the amount of memory
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
The following operators can be used in a hotplug rule expression:
|
||||||
|
.br
|
||||||
|
.RS 2
|
||||||
|
.B + * ( ) / - < >
|
||||||
|
.RE
|
||||||
|
.br
|
||||||
|
Furthermore, the boolean operators \fB & \fP (and) \fB|\fP (or) and \fB!\fP
|
||||||
|
(not) can be used.
|
||||||
|
|
||||||
|
If both HOTPLUG and HOTUNPLUG evaluate to true, only the HOTPLUG action is
|
||||||
|
triggered. If both MEMPLUG and MEMUNPLUG evaluate to true, only the MEMPLUG
|
||||||
|
action is triggered.
|
||||||
|
.
|
||||||
|
.SS "Pre-defined static variables"
|
||||||
|
The following pre-defined variables can be set only to a static, positive,
|
||||||
|
numeric value:
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCPU_MIN\fP - the minimum number of CPUs to keep online (> 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCPU_MAX\fP - the maximum number of CPUs to enable (>= 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBUPDATE\fP - the interval at which cpuplugd evaluates the rules (in seconds,
|
||||||
|
> 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCMM_MIN\fP - the minimum size of the CMM page pool (>= 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCMM_MAX\fP - the maximum size of the CMM page pool (>= 0)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
If the value of CPU_MAX is 0, the overall number of CPUs found in this system
|
||||||
|
is used as the maximum.
|
||||||
|
.
|
||||||
|
.SS "Pre-defined dynamic variables"
|
||||||
|
The following pre-defined variables can either be set to a static value or to an
|
||||||
|
algebraic expression:
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCMM_INC\fP - the amount of pages by which the CMM page pool is increased
|
||||||
|
if the MEMUNPLUG rule is matched (available system memory is decreased).
|
||||||
|
.IP "-" 2
|
||||||
|
\fBCMM_DEC\fP - the amount of pages by which the CMM page pool is decreased
|
||||||
|
if the MEMPLUG rule is matched (available system memory is increased).
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
The following operators can be used in a dynamic variable expression:
|
||||||
|
.br
|
||||||
|
.RS 2
|
||||||
|
.B + * ( ) / - < >
|
||||||
|
.RE
|
||||||
|
.br
|
||||||
|
.
|
||||||
|
.SS "User-defined variables"
|
||||||
|
You can specify complex calculations as user-defined variables, which can then
|
||||||
|
be used in expressions. User-defined variables are case-sensitive and must not
|
||||||
|
match a pre-defined variable or keyword. In the configuration file, definitions
|
||||||
|
for user-defined variables must precede their use in expressions.
|
||||||
|
|
||||||
|
Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and
|
||||||
|
the "_" character, see section \fB"EXAMPLES"\fP for an example (pgscanrate). The
|
||||||
|
maximum name
|
||||||
|
length for a variable is 128 characters, and the maximum total size for all
|
||||||
|
user-defined variables (names + values) is 4096 characters.
|
||||||
|
.
|
||||||
|
.SS "Keywords for CPU hotplug rules"
|
||||||
|
The \fBHOTPLUG\fP and \fBHOTUNPLUG\fP rules can contain the following
|
||||||
|
pre-defined keywords:
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBloadavg\fP - the current load average
|
||||||
|
.IP "-" 2
|
||||||
|
\fBonumcpus\fP - the current number of CPUs which are online
|
||||||
|
.IP "-" 2
|
||||||
|
\fBrunnable_proc\fP - the current amount of runnable processes
|
||||||
|
.IP "-" 2
|
||||||
|
\fBuser\fP - the current CPU user percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBnice\fP - the current CPU nice percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBsystem\fP - the current CPU system percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBidle\fP - the current CPU idle percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBiowait\fP - the current CPU iowait percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBirq\fP - the current CPU irq percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBsoftirq\fP - the current CPU softirq percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBsteal\fP - the current CPU steal percentage
|
||||||
|
.IP "-" 2
|
||||||
|
\fBguest\fP - the current CPU guest percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBguest_nice\fP - the current CPU guest_nice percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBcpustat.<name>\fP - data from /proc/stat and /proc/loadavg
|
||||||
|
.IP "-" 2
|
||||||
|
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
|
||||||
|
Epoch (1970-01-01 00:00:00 +0000 (UTC))
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
The percentage values are accumulated over all online CPUs, so they can vary
|
||||||
|
between 0 and (100 * \fBonumcpus\fP).
|
||||||
|
|
||||||
|
CPU usage data from /proc/stat and /proc/loadavg is accessible by
|
||||||
|
specifying \fBcpustat.<name>\fP, where \fB<name>\fP can be any of the keywords
|
||||||
|
described above, plus \fBtotal_ticks\fP. In this case, \fBloadavg\fP,
|
||||||
|
\fBonumcpus\fP and \fBrunnable_proc\fP
|
||||||
|
provide the same values as the pre-defined keywords, while the others refer
|
||||||
|
to the raw timer ticks as reported by /proc/stat, not the percentage.
|
||||||
|
For example, \fBcpustat.idle\fP reports the timer ticks spent in idle since
|
||||||
|
system start, and \fBcpustat.total_ticks\fP indicates the sum of all reported
|
||||||
|
timer ticks, which can be useful for user-defined percentage calculations.
|
||||||
|
.
|
||||||
|
.SS "Keywords for memory hotplug rules"
|
||||||
|
The \fBMEMPLUG\fP and \fBMEMUNPLUG\fP rules can contain the following
|
||||||
|
pre-defined keywords:
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBapcr\fP - the amount of page cache operations, i.e. pgpin + pgpout from
|
||||||
|
/proc/vmstat (in 512 byte blocks / second)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBfreemem\fP - the amount of free memory (in megabytes)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBswaprate\fP - the number of swap operations, i.e. pswpin + pswpout from
|
||||||
|
/proc/vmstat (in pages / second)
|
||||||
|
.IP "-" 2
|
||||||
|
\fBmeminfo.<name>\fP - any value from /proc/meminfo
|
||||||
|
.IP "-" 2
|
||||||
|
\fBvmstat.<name>\fP - any value from /proc/vmstat
|
||||||
|
.IP "-" 2
|
||||||
|
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
|
||||||
|
Epoch (1970-01-01 00:00:00 +0000 (UTC))
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
All values from /proc/meminfo and /proc/vmstat can be used in an expression
|
||||||
|
by specifying \fBmeminfo.<name>\fP or \fBvmstat.<name>\fP, where \fB<name>\fP
|
||||||
|
matches a symbol name reported by /proc/meminfo or /proc/vmstat (case
|
||||||
|
sensitive), e.g. \fBmeminfo.MemTotal\fP.
|
||||||
|
.
|
||||||
|
.SS "History function"
|
||||||
|
There is a history function for the following keywords:
|
||||||
|
.
|
||||||
|
.RS 2
|
||||||
|
.IP "-" 2
|
||||||
|
\fBcpustat.<name>\fP - data from /proc/stat and /proc/loadavg
|
||||||
|
.IP "-" 2
|
||||||
|
\fBmeminfo.<name>\fP - any value from /proc/meminfo
|
||||||
|
.IP "-" 2
|
||||||
|
\fBvmstat.<name>\fP - any value from /proc/vmstat
|
||||||
|
.IP "-" 2
|
||||||
|
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
|
||||||
|
Epoch (1970-01-01 00:00:00 +0000 (UTC))
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
The history levels can be accessed by appending \fB[<history level>]\fP to the
|
||||||
|
name, where \fB<history level>\fP indicates the amount of past intervals where
|
||||||
|
the value was gathered. [0] means the current interval (the [0] can be omitted
|
||||||
|
in this case), [1] means the previous interval, [2] means two intervals ago,
|
||||||
|
and so on. The history limit is 100. For example, \fBcpustat.system[1]\fP would
|
||||||
|
indicate the system value from /proc/stat at the previous interval, while
|
||||||
|
\fBvmstat.pgpgin\fP and \fBvmstat.pgpgin[0]\fP would both mean the current
|
||||||
|
pgpgin value from /proc/vmstat.
|
||||||
|
|
||||||
|
The \fBtime\fP keyword and its history values can be used to calculate values
|
||||||
|
dependent on time intervals, see section \fB"EXAMPLES"\fP for an example
|
||||||
|
(pgscanrate).
|
||||||
|
.
|
||||||
|
.SH EXAMPLES
|
||||||
|
A complete configuration file could look like this:
|
||||||
|
|
||||||
|
.nf
|
||||||
|
------------------------------ config file start ------------------------------
|
||||||
|
UPDATE="5"
|
||||||
|
CPU_MIN="2"
|
||||||
|
CPU_MAX="5"
|
||||||
|
CMM_MIN="0"
|
||||||
|
CMM_MAX="131072" # 512 MB
|
||||||
|
|
||||||
|
pgscan_k="vmstat.pgscan_kswapd_dma + vmstat.pgscan_kswapd_normal + vmstat.pgscan_kswapd_movable"
|
||||||
|
pgscan_d="vmstat.pgscan_direct_dma + vmstat.pgscan_direct_normal + vmstat.pgscan_direct_movable"
|
||||||
|
pgscan_k1="vmstat.pgscan_kswapd_dma[1] + vmstat.pgscan_kswapd_normal[1] + vmstat.pgscan_kswapd_movable[1]"
|
||||||
|
pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]"
|
||||||
|
pgscanrate="(pgscan_k + pgscan_d - pgscan_k1 - pgscan_d1) / (time - time[1])"
|
||||||
|
cache="meminfo.Cached + meminfo.Buffers"
|
||||||
|
|
||||||
|
# CMM_INC: 10% of free memory + cache, in 4K pages
|
||||||
|
CMM_INC="(meminfo.MemFree + cache) / 40"
|
||||||
|
# CMM_DEC: 10% of total memory in 4K pages
|
||||||
|
CMM_DEC="meminfo.MemTotal / 40"
|
||||||
|
|
||||||
|
HOTPLUG = "(loadavg > onumcpus + 0.75) & (idle < 10.0)"
|
||||||
|
HOTUNPLUG = "(loadavg < onumcpus - 0.25) | (idle > 50)"
|
||||||
|
|
||||||
|
# Plug memory if page scan rate is above 20 pages / sec
|
||||||
|
MEMPLUG = "pgscanrate > 20"
|
||||||
|
# Unplug memory while free memory is above 10% of total memory, or cache uses
|
||||||
|
# more than 50% of total memory
|
||||||
|
MEMUNPLUG = "(meminfo.MemFree > meminfo.MemTotal / 10) | (cache > meminfo.MemTotal / 2)"
|
||||||
|
------------------------------ config file end ------------------------------
|
||||||
|
.fi
|
||||||
|
|
||||||
|
The example includes multiple user-defined variables to calculate the page scan
|
||||||
|
rate with values from /proc/vmstat, as well as the cache size.
|
||||||
|
|
||||||
|
\fBAttention:\fP Do not use these example rules on production systems. The
|
||||||
|
rules have been designed to illustrate the configuration file syntax and are
|
||||||
|
not suitable for actually governing hotplug actions. Useful rules differ
|
||||||
|
considerably depending on the workload, resources, and requirements of the
|
||||||
|
system they are designed for.
|
||||||
|
.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR cpuplugd (8)
|
||||||
82
cpuplugd/mem.c
Normal file
82
cpuplugd/mem.c
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* cmm functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The cmm_pages value defines the size of the balloon of blocked memory.
|
||||||
|
* Increasing the value is removing memory from Linux, which is an memunplug.
|
||||||
|
* Decreasing the value is adding memory back to Linux, which is memplug.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the value of cmm_pages
|
||||||
|
*/
|
||||||
|
void set_cmm_pages(long pages)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
|
||||||
|
filp = fopen("/proc/sys/vm/cmm_pages", "w");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("Cannot open /proc/sys/vm/cmmpages: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
cpuplugd_debug("changing number of pages permanently reserved to %ld\n",
|
||||||
|
pages);
|
||||||
|
fprintf(filp, "%ld\n", pages);
|
||||||
|
fclose(filp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read number of pages permanently reserved
|
||||||
|
*/
|
||||||
|
long get_cmmpages_size()
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
long size;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
filp = fopen("/proc/sys/vm/cmm_pages", "r");
|
||||||
|
if (!filp)
|
||||||
|
cpuplugd_exit("Cannot open /proc/sys/vm/cmm_pages: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
rc = fscanf(filp, "%ld", &size);
|
||||||
|
if (rc == 0)
|
||||||
|
cpuplugd_exit("Can not read /proc/sys/vm/cmm_pages: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
fclose(filp);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset cmm pagesize to value we found prior to daemon startup
|
||||||
|
*/
|
||||||
|
void cleanup_cmm()
|
||||||
|
{
|
||||||
|
set_cmm_pages(cmm_pagesize_start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to check if the cmm kernel module is loaded and the required
|
||||||
|
* files below /proc exit
|
||||||
|
*/
|
||||||
|
int check_cmmfiles(void)
|
||||||
|
{
|
||||||
|
FILE *filp;
|
||||||
|
|
||||||
|
filp = fopen("/proc/sys/vm/cmm_pages", "r");
|
||||||
|
if (!filp)
|
||||||
|
return -1;
|
||||||
|
fclose(filp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
550
cpuplugd/terms.c
Normal file
550
cpuplugd/terms.c
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
/*
|
||||||
|
* cpuplugd - Linux for System z Hotplug Daemon
|
||||||
|
*
|
||||||
|
* Term parsing
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cpuplugd.h"
|
||||||
|
|
||||||
|
static enum op_prio op_prio_table[] =
|
||||||
|
{
|
||||||
|
[OP_NEG] = OP_PRIO_ADD,
|
||||||
|
[OP_GREATER] = OP_PRIO_CMP,
|
||||||
|
[OP_LESSER] = OP_PRIO_CMP,
|
||||||
|
[OP_PLUS] = OP_PRIO_ADD,
|
||||||
|
[OP_MINUS] = OP_PRIO_ADD,
|
||||||
|
[OP_MULT] = OP_PRIO_MULT,
|
||||||
|
[OP_DIV] = OP_PRIO_MULT,
|
||||||
|
[OP_AND] = OP_PRIO_AND,
|
||||||
|
[OP_OR] = OP_PRIO_OR,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_term(struct term *fn)
|
||||||
|
{
|
||||||
|
if (!fn)
|
||||||
|
return;
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_SYMBOL_LOADAVG:
|
||||||
|
case OP_SYMBOL_RUNABLE:
|
||||||
|
case OP_SYMBOL_CPUS:
|
||||||
|
case OP_SYMBOL_USER:
|
||||||
|
case OP_SYMBOL_NICE:
|
||||||
|
case OP_SYMBOL_SYSTEM:
|
||||||
|
case OP_SYMBOL_IDLE:
|
||||||
|
case OP_SYMBOL_IOWAIT:
|
||||||
|
case OP_SYMBOL_IRQ:
|
||||||
|
case OP_SYMBOL_SOFTIRQ:
|
||||||
|
case OP_SYMBOL_STEAL:
|
||||||
|
case OP_SYMBOL_GUEST:
|
||||||
|
case OP_SYMBOL_GUEST_NICE:
|
||||||
|
case OP_CONST:
|
||||||
|
free(fn);
|
||||||
|
break;
|
||||||
|
case OP_NEG:
|
||||||
|
case OP_NOT:
|
||||||
|
free_term(fn->left);
|
||||||
|
free(fn);
|
||||||
|
break;
|
||||||
|
case OP_GREATER:
|
||||||
|
case OP_LESSER:
|
||||||
|
case OP_PLUS:
|
||||||
|
case OP_MINUS:
|
||||||
|
case OP_MULT:
|
||||||
|
case OP_DIV:
|
||||||
|
case OP_AND:
|
||||||
|
free_term(fn->left);
|
||||||
|
free_term(fn->right);
|
||||||
|
free(fn);
|
||||||
|
break;
|
||||||
|
case OP_OR:
|
||||||
|
free_term(fn->left);
|
||||||
|
free_term(fn->right);
|
||||||
|
free(fn);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_term(struct term *fn)
|
||||||
|
{
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_SYMBOL_LOADAVG:
|
||||||
|
printf("loadavg");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_RUNABLE:
|
||||||
|
printf("runnable_proc");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_CPUS:
|
||||||
|
printf("onumcpus");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_USER:
|
||||||
|
printf("user");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_NICE:
|
||||||
|
printf("nice");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_SYSTEM:
|
||||||
|
printf("system");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_IDLE:
|
||||||
|
printf("idle");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_IOWAIT:
|
||||||
|
printf("iowait");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_IRQ:
|
||||||
|
printf("irq");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_SOFTIRQ:
|
||||||
|
printf("softirq");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_STEAL:
|
||||||
|
printf("steal");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_GUEST:
|
||||||
|
printf("guest");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_GUEST_NICE:
|
||||||
|
printf("guest_nice");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_SWAPRATE:
|
||||||
|
printf("swaprate");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_FREEMEM:
|
||||||
|
printf("freemem");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_APCR:
|
||||||
|
printf("apcr");
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_MEMINFO:
|
||||||
|
printf("meminfo.%s[%u]", fn->proc_name, fn->index);
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_VMSTAT:
|
||||||
|
printf("vmstat.%s[%u]", fn->proc_name, fn->index);
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_CPUSTAT:
|
||||||
|
printf("cpustat.%s[%u]", fn->proc_name, fn->index);
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_TIME:
|
||||||
|
printf("time[%u]", fn->index);
|
||||||
|
break;
|
||||||
|
case OP_CONST:
|
||||||
|
printf("%f", fn->value);
|
||||||
|
break;
|
||||||
|
case OP_NEG:
|
||||||
|
printf("-(");
|
||||||
|
print_term(fn->left);
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
case OP_NOT:
|
||||||
|
printf("!(");
|
||||||
|
print_term(fn->left);
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
case OP_PLUS:
|
||||||
|
case OP_MINUS:
|
||||||
|
case OP_MULT:
|
||||||
|
case OP_DIV:
|
||||||
|
case OP_AND:
|
||||||
|
case OP_OR:
|
||||||
|
case OP_GREATER:
|
||||||
|
case OP_LESSER:
|
||||||
|
printf("(");
|
||||||
|
print_term(fn->left);
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_AND:
|
||||||
|
printf(") & (");
|
||||||
|
break;
|
||||||
|
case OP_OR:
|
||||||
|
printf(") | (");
|
||||||
|
break;
|
||||||
|
case OP_GREATER:
|
||||||
|
printf(") > (");
|
||||||
|
break;
|
||||||
|
case OP_LESSER:
|
||||||
|
printf(") < (");
|
||||||
|
break;
|
||||||
|
case OP_PLUS:
|
||||||
|
printf(") + (");
|
||||||
|
break;
|
||||||
|
case OP_MINUS:
|
||||||
|
printf(") - (");
|
||||||
|
break;
|
||||||
|
case OP_MULT:
|
||||||
|
printf(") * (");
|
||||||
|
break;
|
||||||
|
case OP_DIV:
|
||||||
|
printf(") / (");
|
||||||
|
break;
|
||||||
|
// TODO OP_CONST, OP_SYMBOL_LOADAVG, ... possible here???
|
||||||
|
case OP_CONST:
|
||||||
|
printf("%f", fn->value);
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_LOADAVG:
|
||||||
|
case OP_SYMBOL_RUNABLE:
|
||||||
|
case OP_SYMBOL_CPUS:
|
||||||
|
case OP_SYMBOL_USER:
|
||||||
|
case OP_SYMBOL_NICE:
|
||||||
|
case OP_SYMBOL_SYSTEM:
|
||||||
|
case OP_SYMBOL_IDLE:
|
||||||
|
case OP_SYMBOL_IOWAIT:
|
||||||
|
case OP_SYMBOL_IRQ:
|
||||||
|
case OP_SYMBOL_SOFTIRQ:
|
||||||
|
case OP_SYMBOL_STEAL:
|
||||||
|
case OP_SYMBOL_GUEST:
|
||||||
|
case OP_SYMBOL_GUEST_NICE:
|
||||||
|
case OP_SYMBOL_APCR:
|
||||||
|
case OP_SYMBOL_SWAPRATE:
|
||||||
|
case OP_SYMBOL_FREEMEM:
|
||||||
|
case OP_SYMBOL_MEMINFO: // TODO use default: ???
|
||||||
|
case OP_SYMBOL_VMSTAT: // TODO use default: ???
|
||||||
|
case OP_SYMBOL_CPUSTAT: // TODO use default: ???
|
||||||
|
case OP_SYMBOL_TIME: // TODO use default: ???
|
||||||
|
case OP_NEG:
|
||||||
|
case OP_NOT:
|
||||||
|
case VAR_LOAD:
|
||||||
|
case VAR_RUN:
|
||||||
|
case VAR_ONLINE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_term(fn->right);
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
case VAR_LOAD:
|
||||||
|
case VAR_RUN:
|
||||||
|
case VAR_ONLINE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct term *parse_var_term(char **p)
|
||||||
|
{
|
||||||
|
char *s, *var_rvalue;
|
||||||
|
struct term *fn;
|
||||||
|
unsigned int length;
|
||||||
|
char var_name[MAX_VARNAME + 1];
|
||||||
|
|
||||||
|
s = *p;
|
||||||
|
length = 0;
|
||||||
|
fn = NULL;
|
||||||
|
while (isalnum(*s) || *s == '_') {
|
||||||
|
var_name[length] = *s;
|
||||||
|
length++;
|
||||||
|
s++;
|
||||||
|
if (length > MAX_VARNAME)
|
||||||
|
cpuplugd_exit("Variable name too long (max. length is "
|
||||||
|
"%i chars): %s\n", MAX_VARNAME, *p);
|
||||||
|
}
|
||||||
|
var_name[length] = '\0';
|
||||||
|
var_rvalue = get_var_rvalue(var_name);
|
||||||
|
if (var_rvalue) {
|
||||||
|
fn = parse_term(&var_rvalue, OP_PRIO_NONE);
|
||||||
|
if (var_rvalue[0] != '\n')
|
||||||
|
cpuplugd_exit("parsing error at %s, position: %s\n",
|
||||||
|
var_name, var_rvalue);
|
||||||
|
*p = s;
|
||||||
|
}
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct term *parse_term(char **p, enum op_prio prio)
|
||||||
|
{
|
||||||
|
struct term *fn, *new;
|
||||||
|
enum operation op;
|
||||||
|
char *s, *endptr;
|
||||||
|
double value;
|
||||||
|
unsigned int length, i, index;
|
||||||
|
|
||||||
|
s = *p;
|
||||||
|
fn = NULL;
|
||||||
|
if (*s == '-') {
|
||||||
|
s++;
|
||||||
|
fn = malloc(sizeof(struct term));
|
||||||
|
if (fn == NULL)
|
||||||
|
goto out_error;
|
||||||
|
if (isdigit(*s)) {
|
||||||
|
value = 0;
|
||||||
|
length = 0;
|
||||||
|
sscanf(s, "%lf%n", &value, &length);
|
||||||
|
fn->op = OP_CONST;
|
||||||
|
fn->value = -value;
|
||||||
|
s += length;
|
||||||
|
} else {
|
||||||
|
fn->op = OP_NEG;
|
||||||
|
fn->left = parse_term(&s, prio);
|
||||||
|
if (fn->left == NULL)
|
||||||
|
goto out_error;
|
||||||
|
}
|
||||||
|
} else if (*s == '!') {
|
||||||
|
s++;
|
||||||
|
fn = malloc(sizeof(struct term));
|
||||||
|
if (fn == NULL)
|
||||||
|
goto out_error;
|
||||||
|
fn->op = OP_NOT;
|
||||||
|
fn->left = parse_term(&s, prio);
|
||||||
|
if (fn->left == NULL)
|
||||||
|
goto out_error;
|
||||||
|
} else if (isdigit(*s)) {
|
||||||
|
value = 0;
|
||||||
|
length = 0;
|
||||||
|
sscanf(s, "%lf%n", &value, &length);
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
s++;
|
||||||
|
fn = malloc(sizeof(struct term));
|
||||||
|
if (fn == NULL)
|
||||||
|
goto out_error;
|
||||||
|
fn->op = OP_CONST;
|
||||||
|
fn->value = value;
|
||||||
|
} else if (*s == '(') {
|
||||||
|
s++;
|
||||||
|
fn = parse_term(&s, OP_PRIO_NONE);
|
||||||
|
if (fn == NULL || *s != ')')
|
||||||
|
goto out_error;
|
||||||
|
s++;
|
||||||
|
} else {
|
||||||
|
/* Check for variable name */
|
||||||
|
fn = parse_var_term(&s);
|
||||||
|
if (fn == NULL) {
|
||||||
|
for (i = 0; i < sym_names_count; i++)
|
||||||
|
if (strncmp(s, sym_names[i].name,
|
||||||
|
strlen(sym_names[i].name)) == 0)
|
||||||
|
break;
|
||||||
|
if (i >= sym_names_count)
|
||||||
|
/* Term doesn't make sense. */
|
||||||
|
goto out_error;
|
||||||
|
/*
|
||||||
|
* Parse meminfo/vmstat/cpustat with optional history
|
||||||
|
* index [x]
|
||||||
|
*/
|
||||||
|
fn = malloc(sizeof(struct term));
|
||||||
|
if (fn == NULL)
|
||||||
|
goto out_error;
|
||||||
|
fn->op = sym_names[i].symop;
|
||||||
|
s += strlen(sym_names[i].name);
|
||||||
|
length = 0;
|
||||||
|
if (fn->op == OP_SYMBOL_MEMINFO ||
|
||||||
|
fn->op == OP_SYMBOL_VMSTAT ||
|
||||||
|
fn->op == OP_SYMBOL_CPUSTAT) {
|
||||||
|
while (isalpha(s[length]) || s[length] == '_')
|
||||||
|
length++;
|
||||||
|
fn->proc_name = malloc(length + 1);
|
||||||
|
if (fn->proc_name == NULL)
|
||||||
|
goto out_error;
|
||||||
|
strncpy(fn->proc_name, s, length);
|
||||||
|
fn->proc_name[length] = '\0';
|
||||||
|
}
|
||||||
|
if (fn->op == OP_SYMBOL_MEMINFO ||
|
||||||
|
fn->op == OP_SYMBOL_VMSTAT ||
|
||||||
|
fn->op == OP_SYMBOL_CPUSTAT ||
|
||||||
|
fn->op == OP_SYMBOL_TIME) {
|
||||||
|
if (s[length] == '[') {
|
||||||
|
length++;
|
||||||
|
if (!isdigit(s[length]))
|
||||||
|
goto out_error;
|
||||||
|
index = strtol(s + length, &endptr, 10);
|
||||||
|
length = endptr - s;
|
||||||
|
if (s[length] != ']')
|
||||||
|
goto out_error;
|
||||||
|
fn->index = index;
|
||||||
|
if (history_max < index)
|
||||||
|
history_max = index;
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
s += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
switch (*s) {
|
||||||
|
case '>':
|
||||||
|
op = OP_GREATER;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
op = OP_LESSER;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
op = OP_PLUS;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
op = OP_MINUS;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
op = OP_MULT;
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
op = OP_DIV;
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
op = OP_OR;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
op = OP_AND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (prio >= op_prio_table[op])
|
||||||
|
break;
|
||||||
|
s++;
|
||||||
|
new = malloc(sizeof(struct term));
|
||||||
|
new->op = op;
|
||||||
|
new->left = fn;
|
||||||
|
if (new == NULL)
|
||||||
|
goto out_error;
|
||||||
|
new->right = parse_term(&s, op_prio_table[op]);
|
||||||
|
if (new->right == NULL) {
|
||||||
|
free(new);
|
||||||
|
goto out_error;
|
||||||
|
}
|
||||||
|
fn = new;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
*p = s;
|
||||||
|
return fn;
|
||||||
|
out_error:
|
||||||
|
if (fn)
|
||||||
|
free_term(fn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double get_value(struct term *fn)
|
||||||
|
{
|
||||||
|
double value = 0;
|
||||||
|
char *procinfo;
|
||||||
|
unsigned int history_index;
|
||||||
|
|
||||||
|
if (fn->index <= history_current)
|
||||||
|
history_index = history_current - fn->index;
|
||||||
|
else
|
||||||
|
history_index = history_max + 1 - (fn->index - history_current);
|
||||||
|
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_SYMBOL_MEMINFO:
|
||||||
|
procinfo = meminfo + history_index * meminfo_size;
|
||||||
|
value = get_proc_value(procinfo, fn->proc_name, ':');
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_VMSTAT:
|
||||||
|
procinfo = vmstat + history_index * vmstat_size;
|
||||||
|
value = get_proc_value(procinfo, fn->proc_name, ' ');
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_CPUSTAT:
|
||||||
|
procinfo = cpustat + history_index * cpustat_size;
|
||||||
|
value = get_proc_value(procinfo, fn->proc_name, ' ');
|
||||||
|
break;
|
||||||
|
case OP_SYMBOL_TIME:
|
||||||
|
value = timestamps[history_index];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double eval_double(struct term *fn, struct symbols *symbols)
|
||||||
|
{
|
||||||
|
double a, b, sum;
|
||||||
|
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_SYMBOL_LOADAVG:
|
||||||
|
return symbols->loadavg;
|
||||||
|
case OP_SYMBOL_RUNABLE:
|
||||||
|
return symbols->runnable_proc;
|
||||||
|
case OP_SYMBOL_CPUS:
|
||||||
|
return symbols->onumcpus;
|
||||||
|
case OP_SYMBOL_USER:
|
||||||
|
return symbols->user;
|
||||||
|
case OP_SYMBOL_NICE:
|
||||||
|
return symbols->nice;
|
||||||
|
case OP_SYMBOL_SYSTEM:
|
||||||
|
return symbols->system;
|
||||||
|
case OP_SYMBOL_IDLE:
|
||||||
|
return symbols->idle;
|
||||||
|
case OP_SYMBOL_IOWAIT:
|
||||||
|
return symbols->iowait;
|
||||||
|
case OP_SYMBOL_IRQ:
|
||||||
|
return symbols->irq;
|
||||||
|
case OP_SYMBOL_SOFTIRQ:
|
||||||
|
return symbols->softirq;
|
||||||
|
case OP_SYMBOL_STEAL:
|
||||||
|
return symbols->steal;
|
||||||
|
case OP_SYMBOL_GUEST:
|
||||||
|
return symbols->guest;
|
||||||
|
case OP_SYMBOL_GUEST_NICE:
|
||||||
|
return symbols->guest_nice;
|
||||||
|
case OP_SYMBOL_FREEMEM:
|
||||||
|
return symbols->freemem;
|
||||||
|
case OP_SYMBOL_APCR:
|
||||||
|
return symbols->apcr;
|
||||||
|
case OP_SYMBOL_SWAPRATE:
|
||||||
|
return symbols->swaprate;
|
||||||
|
case OP_SYMBOL_MEMINFO:
|
||||||
|
case OP_SYMBOL_VMSTAT:
|
||||||
|
case OP_SYMBOL_CPUSTAT:
|
||||||
|
case OP_SYMBOL_TIME:
|
||||||
|
return get_value(fn);
|
||||||
|
case OP_CONST:
|
||||||
|
return fn->value;
|
||||||
|
case OP_NEG:
|
||||||
|
return -eval_double(fn->left, symbols);
|
||||||
|
case OP_PLUS:
|
||||||
|
return eval_double(fn->left, symbols) +
|
||||||
|
eval_double(fn->right, symbols);
|
||||||
|
case OP_MINUS:
|
||||||
|
return eval_double(fn->left, symbols) -
|
||||||
|
eval_double(fn->right, symbols);
|
||||||
|
case OP_MULT:
|
||||||
|
a = eval_double(fn->left, symbols);
|
||||||
|
b = eval_double(fn->right, symbols);
|
||||||
|
sum = a*b;
|
||||||
|
return sum;
|
||||||
|
/*return eval_double(fn->left, symbols) *
|
||||||
|
eval_double(fn->right, symbols);*/
|
||||||
|
case OP_DIV:
|
||||||
|
a = eval_double(fn->left, symbols);
|
||||||
|
b = eval_double(fn->right, symbols);
|
||||||
|
sum = a/b;
|
||||||
|
return sum;
|
||||||
|
/*return eval_double(fn->left, symbols) /
|
||||||
|
eval_double(fn->right, symbols); */
|
||||||
|
case OP_NOT:
|
||||||
|
case OP_AND:
|
||||||
|
case OP_OR:
|
||||||
|
case OP_GREATER:
|
||||||
|
case OP_LESSER:
|
||||||
|
case VAR_LOAD:
|
||||||
|
case VAR_RUN:
|
||||||
|
case VAR_ONLINE:
|
||||||
|
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int eval_term(struct term *fn, struct symbols *symbols)
|
||||||
|
{
|
||||||
|
if (fn == NULL || symbols == NULL)
|
||||||
|
return 0.0;
|
||||||
|
switch (fn->op) {
|
||||||
|
case OP_NOT:
|
||||||
|
return !eval_term(fn->left, symbols);
|
||||||
|
case OP_OR:
|
||||||
|
return eval_term(fn->left, symbols) == 1 ||
|
||||||
|
eval_term(fn->right, symbols) == 1;
|
||||||
|
case OP_AND:
|
||||||
|
return eval_term(fn->left, symbols) == 1 &&
|
||||||
|
eval_term(fn->right, symbols) == 1;
|
||||||
|
case OP_GREATER:
|
||||||
|
return eval_double(fn->left, symbols) >
|
||||||
|
eval_double(fn->right, symbols);
|
||||||
|
case OP_LESSER:
|
||||||
|
return eval_double(fn->left, symbols) <
|
||||||
|
eval_double(fn->right, symbols);
|
||||||
|
default:
|
||||||
|
return eval_double(fn, symbols) != 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
dasdfmt/Makefile
Normal file
21
dasdfmt/Makefile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
all: dasdfmt
|
||||||
|
|
||||||
|
libs = $(rootdir)/libdasd/libdasd.a \
|
||||||
|
$(rootdir)/libvtoc/libvtoc.a \
|
||||||
|
$(rootdir)/libu2s/libu2s.a \
|
||||||
|
$(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
dasdfmt: dasdfmt.o $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdfmt $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdfmt.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ dasdfmt core
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
195
dasdfmt/dasdfmt.8
Normal file
195
dasdfmt/dasdfmt.8
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH DASDFMT 8 "Apr 2006" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
dasdfmt \- formatting of DASD (ECKD) disk drives.
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\fBdasdfmt\fR [-h] [-t] [-v] [-y] [-p] [-P] [-m \fIstep\fR]
|
||||||
|
.br
|
||||||
|
[-r \fIcylinder\fR] [-b \fIblksize\fR] [-l \fIvolser\fR] [-d \fIlayout\fR]
|
||||||
|
.br
|
||||||
|
[-L] [-V] [-F] [-k] [-C] [-M \fImode\fR] \fIdevice\fR
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it
|
||||||
|
for usage with Linux for S/390.
|
||||||
|
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
|
||||||
|
Any device node created by udev for kernel 2.6 can be used
|
||||||
|
(e.g. '/dev/dasd/0.0.b100/disc').
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in
|
||||||
|
\fBLOSS OF DATA\fR.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB-h\fR or \fB--help\fR
|
||||||
|
Print usage and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-t\fR or \fB--test\fR
|
||||||
|
Disables any modification of the disk drive.
|
||||||
|
.br
|
||||||
|
\fBdasdfmt\fR just prints
|
||||||
|
out, what it \fBwould\fR do.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-v\fR
|
||||||
|
Increases verbosity.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-y\fR
|
||||||
|
Start formatting without further user-confirmation.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB--norecordzero\fR
|
||||||
|
Remove permission for subsystem to format write record zero.
|
||||||
|
.br
|
||||||
|
This is an expert option: Per default in recent dasd drivers, subsystems are
|
||||||
|
granted the permission to format write record zero. This option is used
|
||||||
|
to remove this permission.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-L\fR or \fB--no_label\fR
|
||||||
|
Omit the writing of a disk label after formatting.
|
||||||
|
.br
|
||||||
|
This makes only sense for the 'ldl' disk layout.
|
||||||
|
.br
|
||||||
|
The '-L' option has to be specified after the '-d ldl' option.
|
||||||
|
.br
|
||||||
|
|
||||||
|
e.g. dasdfmt -d ldl -L /dev/...
|
||||||
|
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-V\fR or \fB--version\fR
|
||||||
|
Print version number and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-F\fR or \fB--force\fR
|
||||||
|
Formats the device without performing sanity checking.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-C\fR or \fB--check_host_count\fR
|
||||||
|
Force dasdfmt to check the host access open count to ensure the device
|
||||||
|
is not online on another operating system instance
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-d\fR \fIlayout\fR or \fB--disk_layout\fR=\fIlayout\fR
|
||||||
|
Formats the device with compatible disk layout or linux disk layout.
|
||||||
|
\fIlayout\fR is either \fIcdl\fR for the compatible disk layout
|
||||||
|
(default) or \fIldl\fR for the linux disk layout.
|
||||||
|
.br
|
||||||
|
Compatible disk layout means a special handling of the
|
||||||
|
first two tracks of the volume. This enables other S/390 or zSeries
|
||||||
|
operating systems to access this device (e.g. for backup purposes).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-p\fR or \fB--progressbar\fR
|
||||||
|
Print a progress bar while formatting.
|
||||||
|
Do not use this option if you are using a 3270 console,
|
||||||
|
running in background or redirecting the output to a file.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-P\fR or \fB--percentage\fR
|
||||||
|
Print one line for each formatted cylinder showing the number of the
|
||||||
|
cylinder and percentage of formatting process.
|
||||||
|
Intended to be used by higher level interfaces.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-m\fR \fIstep\fR or \fB--hashmarks\fR=\fIstep\fR
|
||||||
|
Print a hashmark every \fIstep\fR cylinders. The value \fIstep\fR has to be within range [1,1000], otherwise it will be set to the default, which is 10.
|
||||||
|
.br
|
||||||
|
You can use this option to see the progress of formatting in case you
|
||||||
|
are not able to use the progress bar option -p, e.g. with a 3270
|
||||||
|
terminal.
|
||||||
|
.br
|
||||||
|
The value will be at least as big as the -r or --requestsize value.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-M\fR \fImode\fR or \fB--mode\fR=\fImode\fR
|
||||||
|
Specify the \fImode\fR to be used to format the device. Valid modes are:
|
||||||
|
.RS
|
||||||
|
.IP full
|
||||||
|
Format the entire disk with the specified blocksize. (default)
|
||||||
|
.IP quick
|
||||||
|
Format the first two tracks and write label and partition information. Only use
|
||||||
|
this option if you are sure that the target DASD already contains a regular
|
||||||
|
format with the specified blocksize. A blocksize can optionally be specified
|
||||||
|
using \fB-b\fR (\fB--blocksize\fR).
|
||||||
|
.IP expand
|
||||||
|
Format all unformatted tracks at the end of the target DASD. This mode assumes
|
||||||
|
that tracks at the beginning of the DASD volume have already been correctly
|
||||||
|
formatted, while a consecutive set of tracks at the end are unformatted. You can
|
||||||
|
use this mode to make added space available for Linux use after dynamically
|
||||||
|
increasing the size of a DASD volume. A blocksize can optionally be specified
|
||||||
|
using \fB-b\fR (\fB--blocksize\fR).
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB--check\fR
|
||||||
|
Perform a complete format check on a DASD volume. A blocksize can be specified
|
||||||
|
with \fB-b\fR (\fB--blocksize\fR).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR
|
||||||
|
Number of cylinders to be processed in one formatting step.
|
||||||
|
The value must be an integer in the range 1 - 255.
|
||||||
|
.br
|
||||||
|
Use this parameter to exploit any available PAV devices.
|
||||||
|
The number of cylinders optimally matches the number of associated
|
||||||
|
devices, counting the base device and all alias devices.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-b\fR \fIblksize\fR or \fB--blocksize\fR=\fIblksize\fR
|
||||||
|
Specify blocksize to be used. \fIblksize\fR must be a positive integer
|
||||||
|
and always be a power of two. The recommended blocksize is 4096 bytes.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-l\fR \fIvolser\fR or \fB--label\fR=\fIvolser\fR
|
||||||
|
Specify the volume serial number or volume identifier to be written
|
||||||
|
to disk after formatting. If no label is specified, a sensible default
|
||||||
|
is used. \fIvolser\fR is interpreted as ASCII string and is automatically
|
||||||
|
converted to uppercase and then to EBCDIC.
|
||||||
|
.br
|
||||||
|
|
||||||
|
e.g. -l LNX001 or --label=DASD01
|
||||||
|
.br
|
||||||
|
|
||||||
|
The \fIvolser\fR identifies by serial number the volume. A volume serial
|
||||||
|
number is 1 through 6 alphanumeric or one of the following special
|
||||||
|
characters: $, #, @, %. Enclose a serial number that contains special
|
||||||
|
characters in apostrophes. If the number is shorter than six
|
||||||
|
characters, it is padded with trailing blanks.
|
||||||
|
.br
|
||||||
|
|
||||||
|
Do not code a volume serial number as SCRTCH, PRIVAT, or Lnnnnn (L with
|
||||||
|
five numbers); these are used in OS/390 messages to ask the operator to
|
||||||
|
mount a volume. Do not code a volume serial number as MIGRAT, which is
|
||||||
|
used by the OS/390 Hierarchical Storage Manager DFSMShsm for migrated
|
||||||
|
data sets.
|
||||||
|
.br
|
||||||
|
|
||||||
|
NOTE: Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser.
|
||||||
|
.br
|
||||||
|
In case you really have to use special characters, make sure you are using quotes. In addition there is a special handling for the '$' sign. Please specify it using '\\$' if necessary.
|
||||||
|
.br
|
||||||
|
|
||||||
|
e.g. -l 'a@b\\$c#' to get A@B$C#
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-k\fR or \fB--keep_volser\fR
|
||||||
|
Keeps the Volume Serial Number, when writing the Volume Label. This is
|
||||||
|
useful, if the Serial Number has been written with a VM Tool and should not
|
||||||
|
be overwritten.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR fdasd (8)
|
||||||
1713
dasdfmt/dasdfmt.c
Normal file
1713
dasdfmt/dasdfmt.c
Normal file
File diff suppressed because it is too large
Load Diff
350
dasdfmt/dasdfmt.h
Normal file
350
dasdfmt/dasdfmt.h
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
/*
|
||||||
|
* dasdfmt - Format DASD ECKD devices for use by Linux
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2002, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DASDFMT_H
|
||||||
|
#define DASDFMT_H
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <mntent.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* SECTION: Definition needed for DASD-API (see dasd.h) *
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#define DASD_IOCTL_LETTER 'D'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct dasd_information2_t
|
||||||
|
* represents any data about the device, which is visible to userspace.
|
||||||
|
* including format and features.
|
||||||
|
*/
|
||||||
|
typedef struct dasd_information2_t {
|
||||||
|
unsigned int devno; /* S/390 devno */
|
||||||
|
unsigned int real_devno; /* for aliases */
|
||||||
|
unsigned int schid; /* S/390 subchannel identifier */
|
||||||
|
unsigned int cu_type : 16; /* from SenseID */
|
||||||
|
unsigned int cu_model : 8; /* from SenseID */
|
||||||
|
unsigned int dev_type : 16; /* from SenseID */
|
||||||
|
unsigned int dev_model : 8; /* from SenseID */
|
||||||
|
unsigned int open_count;
|
||||||
|
unsigned int req_queue_len;
|
||||||
|
unsigned int chanq_len; /* length of chanq */
|
||||||
|
char type[4]; /* from discipline.name, */
|
||||||
|
/* 'none' for unknown */
|
||||||
|
unsigned int status; /* current device level */
|
||||||
|
unsigned int label_block; /* where to find the VOLSER */
|
||||||
|
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
|
||||||
|
unsigned int characteristics_size;
|
||||||
|
unsigned int confdata_size;
|
||||||
|
char characteristics[64]; /* from read_device_characteristics */
|
||||||
|
char configuration_data[256]; /* from read_configuration_data */
|
||||||
|
unsigned int format; /* format info like formatted/cdl/ldl/... */
|
||||||
|
unsigned int features; /* dasd features like 'ro',... */
|
||||||
|
unsigned int reserved0; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved1; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved2; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved3; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved4; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved5; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved6; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved7; /* reserved for further use ,... */
|
||||||
|
} dasd_information2_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct dasd_eckd_characteristics {
|
||||||
|
unsigned short cu_type;
|
||||||
|
struct {
|
||||||
|
unsigned char support:2;
|
||||||
|
unsigned char async:1;
|
||||||
|
unsigned char reserved:1;
|
||||||
|
unsigned char cache_info:1;
|
||||||
|
unsigned char model:3;
|
||||||
|
} __attribute__ ((packed)) cu_model;
|
||||||
|
unsigned short dev_type;
|
||||||
|
unsigned char dev_model;
|
||||||
|
struct {
|
||||||
|
unsigned char mult_burst:1;
|
||||||
|
unsigned char RT_in_LR:1;
|
||||||
|
unsigned char reserved1:1;
|
||||||
|
unsigned char RD_IN_LR:1;
|
||||||
|
unsigned char reserved2:4;
|
||||||
|
unsigned char reserved3:8;
|
||||||
|
unsigned char defect_wr:1;
|
||||||
|
unsigned char XRC_supported:1;
|
||||||
|
unsigned char reserved4:1;
|
||||||
|
unsigned char striping:1;
|
||||||
|
unsigned char reserved5:4;
|
||||||
|
unsigned char cfw:1;
|
||||||
|
unsigned char reserved6:2;
|
||||||
|
unsigned char cache:1;
|
||||||
|
unsigned char dual_copy:1;
|
||||||
|
unsigned char dfw:1;
|
||||||
|
unsigned char reset_alleg:1;
|
||||||
|
unsigned char sense_down:1;
|
||||||
|
} __attribute__ ((packed)) facilities;
|
||||||
|
unsigned char dev_class;
|
||||||
|
unsigned char unit_type;
|
||||||
|
unsigned short no_cyl;
|
||||||
|
unsigned short trk_per_cyl;
|
||||||
|
unsigned char sec_per_trk;
|
||||||
|
unsigned char byte_per_track[3];
|
||||||
|
unsigned short home_bytes;
|
||||||
|
unsigned char formula;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
unsigned char f1;
|
||||||
|
unsigned short f2;
|
||||||
|
unsigned short f3;
|
||||||
|
} __attribute__ ((packed)) f_0x01;
|
||||||
|
struct {
|
||||||
|
unsigned char f1;
|
||||||
|
unsigned char f2;
|
||||||
|
unsigned char f3;
|
||||||
|
unsigned char f4;
|
||||||
|
unsigned char f5;
|
||||||
|
} __attribute__ ((packed)) f_0x02;
|
||||||
|
} __attribute__ ((packed)) factors;
|
||||||
|
unsigned short first_alt_trk;
|
||||||
|
unsigned short no_alt_trk;
|
||||||
|
unsigned short first_dia_trk;
|
||||||
|
unsigned short no_dia_trk;
|
||||||
|
unsigned short first_sup_trk;
|
||||||
|
unsigned short no_sup_trk;
|
||||||
|
unsigned char MDR_ID;
|
||||||
|
unsigned char OBR_ID;
|
||||||
|
unsigned char director;
|
||||||
|
unsigned char rd_trk_set;
|
||||||
|
unsigned short max_rec_zero;
|
||||||
|
unsigned char reserved1;
|
||||||
|
unsigned char RWANY_in_LR;
|
||||||
|
unsigned char factor6;
|
||||||
|
unsigned char factor7;
|
||||||
|
unsigned char factor8;
|
||||||
|
unsigned char reserved2[3];
|
||||||
|
unsigned char reserved3[6];
|
||||||
|
unsigned int long_no_cyl;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents possible format modes that can be specified when formatting
|
||||||
|
* a DASD.
|
||||||
|
*/
|
||||||
|
typedef enum format_mode_t {
|
||||||
|
FULL, /* default mode */
|
||||||
|
QUICK, /* format only the first 2 tracks */
|
||||||
|
EXPAND, /* search for unformatted area and format only that part*/
|
||||||
|
} format_mode_t;
|
||||||
|
|
||||||
|
static const char mode_str[3][10] = {
|
||||||
|
"Full", "Quick", "Expand"
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct format_data_t
|
||||||
|
* represents all data necessary to format a dasd
|
||||||
|
*/
|
||||||
|
typedef struct format_data_t {
|
||||||
|
unsigned int start_unit; /* from track */
|
||||||
|
unsigned int stop_unit; /* to track */
|
||||||
|
unsigned int blksize; /* sectorsize */
|
||||||
|
unsigned int intensity;
|
||||||
|
} format_data_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* values to be used for format_data_t.intensity
|
||||||
|
* 0/8: normal format
|
||||||
|
* 1/9: also write record zero
|
||||||
|
* 3/11: also write home address
|
||||||
|
* 4/12: invalidate track
|
||||||
|
*/
|
||||||
|
#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
|
||||||
|
#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
|
||||||
|
#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
|
||||||
|
#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
|
||||||
|
#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct format_check_t
|
||||||
|
* represents all data necessary to evaluate the format of
|
||||||
|
* different tracks of a dasd
|
||||||
|
*/
|
||||||
|
typedef struct format_check_t {
|
||||||
|
/* Input */
|
||||||
|
struct format_data_t expect;
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
unsigned int result; /* Error indication (DASD_FMT_ERR_*) */
|
||||||
|
unsigned int unit; /* Track that is in error */
|
||||||
|
unsigned int rec; /* Record that is in error */
|
||||||
|
unsigned int num_records; /* Records in the track in error */
|
||||||
|
unsigned int blksize; /* Block-size of first record in error */
|
||||||
|
unsigned int key_length; /* Key length of first record in error */
|
||||||
|
} format_check_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* values to be used in format_check_t for indicating
|
||||||
|
* possible format errors
|
||||||
|
*/
|
||||||
|
#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
|
||||||
|
#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
|
||||||
|
#define DASD_FMT_ERR_BLKSIZE 3
|
||||||
|
#define DASD_FMT_ERR_RECORD_ID 4
|
||||||
|
#define DASD_FMT_ERR_KEY_LENGTH 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
* values to be used for dasd_information2_t.format
|
||||||
|
* 0x00: NOT formatted
|
||||||
|
* 0x01: Linux disc layout
|
||||||
|
* 0x02: Common disc layout
|
||||||
|
*/
|
||||||
|
#define DASD_FORMAT_NONE 0
|
||||||
|
#define DASD_FORMAT_LDL 1
|
||||||
|
#define DASD_FORMAT_CDL 2
|
||||||
|
|
||||||
|
/* Disable the volume (for Linux) */
|
||||||
|
#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER,0)
|
||||||
|
/* Enable the volume (for Linux) */
|
||||||
|
#define BIODASDENABLE _IO(DASD_IOCTL_LETTER,1)
|
||||||
|
|
||||||
|
/* Get information on a dasd device (enhanced) */
|
||||||
|
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t)
|
||||||
|
|
||||||
|
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
|
||||||
|
#define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t)
|
||||||
|
|
||||||
|
/* Check device format according to format_data_t */
|
||||||
|
#define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t)
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h ) *
|
||||||
|
****************************************************************************/
|
||||||
|
/* re-read partition table */
|
||||||
|
#define BLKRRPART _IO(0x12,95)
|
||||||
|
/* get block device sector size */
|
||||||
|
#define BLKSSZGET _IO(0x12,104)
|
||||||
|
/* get read-only status (0 = read_write) */
|
||||||
|
#define BLKROGET _IO(0x12,94)
|
||||||
|
|
||||||
|
/* get device geometry */
|
||||||
|
#define HDIO_GETGEO 0x0301
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* SECTION: DASDFMT internal types *
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#define DASD_PARTN_BITS 2
|
||||||
|
#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
|
||||||
|
|
||||||
|
#define EXIT_MISUSE 1
|
||||||
|
#define EXIT_BUSY 2
|
||||||
|
#define LABEL_LENGTH 14
|
||||||
|
#define VLABEL_CHARS 84
|
||||||
|
#define LINE_LENGTH 80
|
||||||
|
#define ERR_LENGTH 90
|
||||||
|
|
||||||
|
#define DEFAULT_BLOCKSIZE 4096
|
||||||
|
/* requestsize - number of cylinders in one format step */
|
||||||
|
#define DEFAULT_REQUESTSIZE 10
|
||||||
|
#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1)
|
||||||
|
|
||||||
|
#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);}
|
||||||
|
#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);}
|
||||||
|
|
||||||
|
#define CHECK_SPEC_MAX_ONCE(i,str) \
|
||||||
|
{if (i>1) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
|
||||||
|
"can only be specified once\n",prog_name);}
|
||||||
|
|
||||||
|
#define PARSE_PARAM_INTO(x,param,base,str) \
|
||||||
|
{char *endptr=NULL; x=(int)strtol(param,&endptr,base); \
|
||||||
|
if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
|
||||||
|
"is in invalid format\n",prog_name);}
|
||||||
|
|
||||||
|
typedef struct bootstrap1 {
|
||||||
|
u_int32_t key;
|
||||||
|
u_int32_t data[6];
|
||||||
|
} __attribute__ ((packed)) bootstrap1_t;
|
||||||
|
|
||||||
|
typedef struct bootstrap2 {
|
||||||
|
u_int32_t key;
|
||||||
|
u_int32_t data[36];
|
||||||
|
} __attribute__ ((packed)) bootstrap2_t;
|
||||||
|
|
||||||
|
typedef struct dasdfmt_info {
|
||||||
|
dasd_information2_t dasd_info;
|
||||||
|
char devname[PATH_MAX];
|
||||||
|
int verbosity;
|
||||||
|
int testmode;
|
||||||
|
int withoutprompt;
|
||||||
|
int print_progressbar;
|
||||||
|
int print_hashmarks, hashstep;
|
||||||
|
int print_percentage;
|
||||||
|
int force;
|
||||||
|
int writenolabel;
|
||||||
|
int labelspec;
|
||||||
|
int cdl_format;
|
||||||
|
int blksize_specified;
|
||||||
|
int reqsize_specified;
|
||||||
|
int device_id;
|
||||||
|
int keep_volser;
|
||||||
|
int force_host;
|
||||||
|
int layout_specified;
|
||||||
|
int check;
|
||||||
|
} dasdfmt_info_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
C9D7D3F1 000A0000 0000000F 03000000 00000001 00000000 00000000
|
||||||
|
*/
|
||||||
|
static bootstrap1_t ipl1 = {
|
||||||
|
0xC9D7D3F1, {
|
||||||
|
0x000A0000, 0x0000000F, 0x03000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
C9D7D3F2 07003AB8 40000006 31003ABE 40000005 08003AA0 00000000 06000000
|
||||||
|
20000000 00000000 00000000 00000400 00000000 00000000 00000000 00000000
|
||||||
|
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||||
|
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||||
|
00000000 00000000 00000000 00000000 00000000
|
||||||
|
*/
|
||||||
|
static bootstrap2_t ipl2 = {
|
||||||
|
0xC9D7D3F2, {
|
||||||
|
0x07003AB8, 0x40000006, 0x31003ABE,
|
||||||
|
0x40000005, 0x08003AA0, 0x00000000,
|
||||||
|
0x06000000, 0x20000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000400, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DASDFMT_H */
|
||||||
|
|
||||||
20
dasdinfo/Makefile
Normal file
20
dasdinfo/Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
libs = $(rootdir)/libu2s/libu2s.a \
|
||||||
|
$(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
all: dasdinfo
|
||||||
|
|
||||||
|
dasdinfo: dasdinfo.o $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdinfo $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdinfo.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ dasdinfo core
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
97
dasdinfo/dasdinfo.8
Normal file
97
dasdinfo/dasdinfo.8
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH DASDINFO 8 "Febr 2007" "s390-tools" "Linux Administrator's Manual"
|
||||||
|
.SH NAME
|
||||||
|
.B "dasdinfo "
|
||||||
|
\- tool to read unique id from s390 DASD device
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.BI "dasdinfo [-a] [-l] [-u] [-x] [-e] {-i " <busid>
|
||||||
|
.BI "| -b " <blockdev>
|
||||||
|
.BI " | -d " <devnode>
|
||||||
|
.BI "}"
|
||||||
|
.sp
|
||||||
|
.BI "dasdinfo [-h] [-v]"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B dasdinfo
|
||||||
|
displays specific information about a specified DASD device.
|
||||||
|
It is normally called from a udev rule, to provide udev with a unique id string and
|
||||||
|
additional information (type, serial) for an S390 DASD drive. Udev can use this
|
||||||
|
information to create symlinks in /dev/disk/by-id and /dev/disk/by-label
|
||||||
|
to the real device node.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "-a|--all"
|
||||||
|
Same as -u -x -l
|
||||||
|
.TP
|
||||||
|
.BI "-x|--extended-uid"
|
||||||
|
Print DASD uid
|
||||||
|
|
||||||
|
This option prints the full uid of the DASD. When z/VM provides two
|
||||||
|
virtual devices that are actually located on the same real device, the
|
||||||
|
first four tokens of the uid will be identical for both devices. z/VM
|
||||||
|
may provide an additional token that allows to distinguish between
|
||||||
|
different minidisks. You need both support in the Linux kernel and
|
||||||
|
z/VM to receive such an additional token.
|
||||||
|
|
||||||
|
For z/VM: VM support for the hypervisor injected Special Node Element
|
||||||
|
Qualifier (SNEQ) (or hypervisor injected self-description data) is
|
||||||
|
available by applying the PTFs for VM APAR VM64273 on z/VM 5.2.0 and higher.
|
||||||
|
.TP
|
||||||
|
.BI "-u|--uid"
|
||||||
|
Print DASD uid without z/VM minidisk token
|
||||||
|
|
||||||
|
z/VM may provide an additional token that allows to distinguish
|
||||||
|
between different minidisks (see --extended-uid option). To remain
|
||||||
|
compatibile with systems that were installed on older Linux or z/VM
|
||||||
|
levels, the -u option will print the uid excluding any z/VM-provided
|
||||||
|
minidisk token.
|
||||||
|
|
||||||
|
For example, if the extended uid is
|
||||||
|
IBM.75000000092461.e900.10.00000000000037400000000000000000 then the
|
||||||
|
uid is IBM.75000000092461.e900.10. If the extended uid contains no
|
||||||
|
minidisk token, e.g. in an LPAR environment, then both uids are the
|
||||||
|
same.
|
||||||
|
.TP
|
||||||
|
.BI "-l|--label"
|
||||||
|
Print DASD volume label (volser).
|
||||||
|
.TP
|
||||||
|
.BI "-i|--busid " <busid>
|
||||||
|
Use the bus ID as input parameter, e.g. 0.0.e910.
|
||||||
|
.TP
|
||||||
|
.BI "-b|--block " <blockdev>
|
||||||
|
Use the block device name as input parameter, e.g. dasdb.
|
||||||
|
.TP
|
||||||
|
.BI "-d|--devnode " <devnode>
|
||||||
|
Use a device node as input parameter, e.g. /dev/dasdb.
|
||||||
|
.TP
|
||||||
|
.BI "-e|--export"
|
||||||
|
Print all values (ID_BUS, ID_TYPE, ID_SERIAL).
|
||||||
|
.TP
|
||||||
|
.BI "-h|--help"
|
||||||
|
Print usage text.
|
||||||
|
.TP
|
||||||
|
.BI "-v|--version"
|
||||||
|
Print version number.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
dasdinfo -u -i 0.0.e910
|
||||||
|
|
||||||
|
dasdinfo -u -b dasdb
|
||||||
|
|
||||||
|
dasdinfo -u -d /dev/dasdb
|
||||||
|
|
||||||
|
All three examples should return the same unique ID for
|
||||||
|
the same DASD device, e.g. IBM.75000000092461.e900.10.
|
||||||
|
|
||||||
|
In case this uid is not available, dasdinfo will return
|
||||||
|
the volume label instead, e.g. 0XE910.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR udev (7)
|
||||||
|
.SH AUTHORS
|
||||||
|
Volker Sameske <sameske@de.ibm.com>
|
||||||
|
|
||||||
837
dasdinfo/dasdinfo.c
Normal file
837
dasdinfo/dasdinfo.c
Normal file
@@ -0,0 +1,837 @@
|
|||||||
|
/*
|
||||||
|
* dasdinfo - Display unique DASD ID, either UID or volser
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2007, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#define READCHUNK 80
|
||||||
|
#define BLKSSZGET _IO(0x12,104)
|
||||||
|
#define DASD_IOCTL_LETTER 'D'
|
||||||
|
#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,struct dasd_information)
|
||||||
|
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,struct dasd_information2)
|
||||||
|
#define TEMP_DEV_MAX_RETRIES 1000
|
||||||
|
|
||||||
|
static const struct util_prg prg = {
|
||||||
|
.desc = "Display DASD volume serial number and ID information",
|
||||||
|
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
|
||||||
|
.copyright_vec = {
|
||||||
|
{
|
||||||
|
.owner = "IBM Corp.",
|
||||||
|
.pub_first = 2007,
|
||||||
|
},
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("DEVICE"),
|
||||||
|
{
|
||||||
|
.option = { "block", required_argument, NULL, 'b' },
|
||||||
|
.argument = "BLOCKDEV",
|
||||||
|
.desc = "Block device name, e.g. dasdb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "devnode", required_argument, NULL, 'd' },
|
||||||
|
.argument = "DEVNODE",
|
||||||
|
.desc = "Device node, e.g. /dev/dasda",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "busid", required_argument, NULL, 'i' },
|
||||||
|
.argument = "BUSID",
|
||||||
|
.desc = "Bus ID, e.g. 0.0.e910",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "label", no_argument, NULL, 'l' },
|
||||||
|
.desc = "Print DASD volume label (volser)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "uid", no_argument, NULL, 'u' },
|
||||||
|
.desc = "Print DASD uid (without z/VM minidisk token)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "extended-uid", no_argument, NULL, 'x' },
|
||||||
|
.desc = "Print DASD uid (including z/VM minidisk token)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "all", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Same as -u -x -l",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "export", no_argument, NULL, 'e' },
|
||||||
|
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
/* needed because ftw can not pass arbitrary arguments */
|
||||||
|
static char *searchbusid;
|
||||||
|
static char *busiddir;
|
||||||
|
|
||||||
|
struct volume_label {
|
||||||
|
char volkey[4];
|
||||||
|
char vollbl[4];
|
||||||
|
char volid[6];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct dasd_information2 {
|
||||||
|
unsigned int devno;
|
||||||
|
unsigned int real_devno;
|
||||||
|
unsigned int schid;
|
||||||
|
unsigned int cu_type : 16;
|
||||||
|
unsigned int cu_model : 8;
|
||||||
|
unsigned int dev_type : 16;
|
||||||
|
unsigned int dev_model : 8;
|
||||||
|
unsigned int open_count;
|
||||||
|
unsigned int req_queue_len;
|
||||||
|
unsigned int chanq_len;
|
||||||
|
char type[4];
|
||||||
|
unsigned int status;
|
||||||
|
unsigned int label_block;
|
||||||
|
unsigned int FBA_layout;
|
||||||
|
unsigned int characteristics_size;
|
||||||
|
unsigned int confdata_size;
|
||||||
|
char characteristics[64];
|
||||||
|
char configuration_data[256];
|
||||||
|
unsigned int format;
|
||||||
|
unsigned int features;
|
||||||
|
unsigned int reserved0;
|
||||||
|
unsigned int reserved1;
|
||||||
|
unsigned int reserved2;
|
||||||
|
unsigned int reserved3;
|
||||||
|
unsigned int reserved4;
|
||||||
|
unsigned int reserved5;
|
||||||
|
unsigned int reserved6;
|
||||||
|
unsigned int reserved7;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dasd_information {
|
||||||
|
unsigned int devno;
|
||||||
|
unsigned int real_devno;
|
||||||
|
unsigned int schid;
|
||||||
|
unsigned int cu_type : 16;
|
||||||
|
unsigned int cu_model : 8;
|
||||||
|
unsigned int dev_type : 16;
|
||||||
|
unsigned int dev_model : 8;
|
||||||
|
unsigned int open_count;
|
||||||
|
unsigned int req_queue_len;
|
||||||
|
unsigned int chanq_len;
|
||||||
|
char type[4];
|
||||||
|
unsigned int status;
|
||||||
|
unsigned int label_block;
|
||||||
|
unsigned int FBA_layout;
|
||||||
|
unsigned int characteristics_size;
|
||||||
|
unsigned int confdata_size;
|
||||||
|
char characteristics[64];
|
||||||
|
char configuration_data[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dasd_data
|
||||||
|
{
|
||||||
|
struct dasd_information2 dasd_info;
|
||||||
|
int dasd_info_version;
|
||||||
|
int blksize;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char EBCtoASC[256] =
|
||||||
|
{
|
||||||
|
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||||
|
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||||
|
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
||||||
|
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */
|
||||||
|
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */
|
||||||
|
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
||||||
|
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
||||||
|
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
|
||||||
|
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
||||||
|
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
||||||
|
/* 0x40 SP RSP ? ---- */
|
||||||
|
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
||||||
|
/* 0x48 . < ( + | */
|
||||||
|
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
||||||
|
/* 0x50 & ---- */
|
||||||
|
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
||||||
|
/* 0x58 ? ! $ * ) ; */
|
||||||
|
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
|
||||||
|
/* 0x60 - / ---- ? ---- ---- ---- */
|
||||||
|
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
||||||
|
/* 0x68 ---- , % _ > ? */
|
||||||
|
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
||||||
|
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
|
||||||
|
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
/* 0x78 * ` : # @ ' = " */
|
||||||
|
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
||||||
|
/* 0x80 * a b c d e f g */
|
||||||
|
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||||
|
/* 0x88 h i ---- ---- ---- */
|
||||||
|
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
||||||
|
/* 0x90 ? j k l m n o p */
|
||||||
|
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
||||||
|
/* 0x98 q r ---- ---- */
|
||||||
|
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
||||||
|
/* 0xA0 ~ s t u v w x */
|
||||||
|
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||||
|
/* 0xA8 y z ---- ---- ---- ---- */
|
||||||
|
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
/* 0xB0 ^ ---- ? ---- */
|
||||||
|
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
||||||
|
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
|
||||||
|
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
/* 0xC0 { A B C D E F G */
|
||||||
|
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||||||
|
/* 0xC8 H I ---- ? ---- */
|
||||||
|
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
||||||
|
/* 0xD0 } J K L M N O P */
|
||||||
|
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||||
|
/* 0xD8 Q R ---- ? */
|
||||||
|
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
||||||
|
/* 0xE0 \ S T U V W X */
|
||||||
|
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||||
|
/* 0xE8 Y Z ---- ? ---- ---- ---- */
|
||||||
|
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
||||||
|
/* 0xF0 0 1 2 3 4 5 6 7 */
|
||||||
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||||
|
/* 0xF8 8 9 ---- ---- ? ---- ---- ---- */
|
||||||
|
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *dinfo_ebcdic_dec (char *source, char *target, int l)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < l; i++)
|
||||||
|
target[i]=EBCtoASC[(unsigned char)(source[i])];
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_read_dasd_uid (char *uidfile, char *readbuf, int readbuflen)
|
||||||
|
{
|
||||||
|
FILE *dasduid;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
if ((dasduid = fopen(uidfile,"r")) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (fgets(readbuf + offset, READCHUNK, dasduid) &&
|
||||||
|
readbuf[strlen(readbuf)-1] != '\n' ) {
|
||||||
|
offset += READCHUNK-1;
|
||||||
|
if ( offset+READCHUNK-1 >= readbuflen ) {
|
||||||
|
readbuf = realloc(readbuf,
|
||||||
|
readbuflen + READCHUNK-1);
|
||||||
|
readbuflen += READCHUNK-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(dasduid);
|
||||||
|
|
||||||
|
if (strlen(readbuf) <= 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_read_dasd_vlabel (char *device, struct volume_label *vlabel,
|
||||||
|
char *readbuf)
|
||||||
|
{
|
||||||
|
struct dasd_data data;
|
||||||
|
struct volume_label tmp;
|
||||||
|
int vlsize = sizeof(struct volume_label);
|
||||||
|
unsigned long vlabel_start;
|
||||||
|
char vollbl[5];
|
||||||
|
int f;
|
||||||
|
char *space;
|
||||||
|
|
||||||
|
if ((f = open(device, O_RDONLY)) < 0) {
|
||||||
|
printf("Could not open device node.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(f, BLKSSZGET, &data.blksize) != 0) {
|
||||||
|
printf("Unable to figure out block size.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(f, BIODASDINFO2, &data.dasd_info) == 0)
|
||||||
|
data.dasd_info_version = 2;
|
||||||
|
else {
|
||||||
|
if (ioctl(f, BIODASDINFO, &data.dasd_info) != 0) {
|
||||||
|
printf("Unable to figure out DASD informations.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vlabel_start = data.dasd_info.label_block * data.blksize;
|
||||||
|
if (lseek(f, vlabel_start, SEEK_SET) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
bzero(vlabel, vlsize);
|
||||||
|
|
||||||
|
if (read(f, vlabel, vlsize) != vlsize) {
|
||||||
|
printf("Could not read volume label.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.dasd_info.FBA_layout) {
|
||||||
|
bzero(&tmp, vlsize);
|
||||||
|
memcpy(&tmp, vlabel, vlsize);
|
||||||
|
memcpy(vlabel->vollbl, &tmp, vlsize-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(f);
|
||||||
|
|
||||||
|
bzero(readbuf, 7);
|
||||||
|
bzero(vollbl, 5);
|
||||||
|
strncpy(vollbl, vlabel->vollbl, 4);
|
||||||
|
dinfo_ebcdic_dec(vollbl, vollbl, 4);
|
||||||
|
|
||||||
|
if ((strncmp(vollbl, "VOL1", 4) == 0) ||
|
||||||
|
(strncmp(vollbl, "LNX1", 4) == 0) ||
|
||||||
|
(strncmp(vollbl, "CMS1", 4) == 0)) {
|
||||||
|
strncpy(readbuf, vlabel->volid, 6);
|
||||||
|
dinfo_ebcdic_dec(readbuf, readbuf, 6);
|
||||||
|
space = strchr(readbuf,' ');
|
||||||
|
if (space)
|
||||||
|
*space = 0;
|
||||||
|
} else
|
||||||
|
strcpy(readbuf, "");
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
close(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *dinfo_malloc(size_t size)
|
||||||
|
{
|
||||||
|
void *result;
|
||||||
|
|
||||||
|
result = malloc(size);
|
||||||
|
if (result == NULL) {
|
||||||
|
printf("Could not allocate %lld bytes of memory",
|
||||||
|
(unsigned long long) size);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *dinfo_make_path(char *dirname, char *filename)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen(dirname) + strlen(filename) + 2;
|
||||||
|
result = (char *) dinfo_malloc(len);
|
||||||
|
if (result == NULL)
|
||||||
|
return NULL;
|
||||||
|
sprintf(result, "%s/%s", dirname, filename);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_create_devnode(dev_t dev, char **devno)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
char * pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
|
||||||
|
getenv("HOME"), "." , "/"};
|
||||||
|
char filename[] = "dasdinfo0000";
|
||||||
|
mode_t mode;
|
||||||
|
unsigned int path;
|
||||||
|
int retry;
|
||||||
|
int rc;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
mode = S_IFBLK | S_IRWXU;
|
||||||
|
|
||||||
|
/* Try several locations for the temporary device node. */
|
||||||
|
for (path=0; path < sizeof(pathname) / sizeof(pathname[0]); path++) {
|
||||||
|
if (pathname[path] == NULL)
|
||||||
|
continue;
|
||||||
|
for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||||
|
sprintf(filename, "dasdinfo%04d", retry);
|
||||||
|
result = dinfo_make_path(pathname[path], filename);
|
||||||
|
if (result == NULL)
|
||||||
|
return -1;
|
||||||
|
rc = mknod(result, mode, dev);
|
||||||
|
if (rc == 0) {
|
||||||
|
/* Need this test to cover
|
||||||
|
* 'nodev'-mounted
|
||||||
|
* filesystems. */
|
||||||
|
fd = open(result, O_RDONLY);
|
||||||
|
if (fd != -1) {
|
||||||
|
close(fd);
|
||||||
|
*devno = result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
remove(result);
|
||||||
|
retry = TEMP_DEV_MAX_RETRIES;
|
||||||
|
} else if (errno != EEXIST)
|
||||||
|
retry = TEMP_DEV_MAX_RETRIES;
|
||||||
|
free(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Error: Unable to create temporary device node");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dinfo_free_devnode(char *device)
|
||||||
|
{
|
||||||
|
if (remove(device)) {
|
||||||
|
printf("Warning: Could not remove "
|
||||||
|
"temporary file %s", device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_extract_dev(dev_t *dev, char *str, int readbuflen)
|
||||||
|
{
|
||||||
|
char tmp[readbuflen];
|
||||||
|
char *p = NULL;
|
||||||
|
int ma, mi;
|
||||||
|
|
||||||
|
bzero(tmp, readbuflen);
|
||||||
|
strncpy(tmp, str, readbuflen);
|
||||||
|
if ((p = strchr(tmp, ':')) == NULL) {
|
||||||
|
printf("Error: unable to extract major/minor\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
ma = atoi(tmp);
|
||||||
|
mi = atoi(p + sizeof(char));
|
||||||
|
|
||||||
|
*dev = makedev(ma, mi);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
|
||||||
|
{
|
||||||
|
FILE *dasddev;
|
||||||
|
int offset = 0;
|
||||||
|
char *devfile = NULL;
|
||||||
|
char *readbuf = NULL;
|
||||||
|
int readbuflen = READCHUNK;
|
||||||
|
|
||||||
|
if ((devfile = dinfo_malloc(readbuflen)) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sprintf(devfile,"/sys/block/%s/dev", blockdev);
|
||||||
|
|
||||||
|
if ((readbuf = dinfo_malloc(readbuflen)) == NULL) {
|
||||||
|
printf("Error: Not enough memory to allocate readbuffer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dasddev = fopen(devfile,"r")) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (fgets(readbuf + offset, READCHUNK, dasddev) &&
|
||||||
|
readbuf[strlen(readbuf)-1] != '\n' ) {
|
||||||
|
offset += READCHUNK-1;
|
||||||
|
if (offset+READCHUNK-1 >= readbuflen) {
|
||||||
|
readbuf = realloc(readbuf,
|
||||||
|
readbuflen + READCHUNK-1);
|
||||||
|
readbuflen += READCHUNK-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(dasddev);
|
||||||
|
|
||||||
|
if (dinfo_extract_dev(dev, readbuf, readbuflen) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
||||||
|
int tflag, struct FTW *ftwbuf)
|
||||||
|
{
|
||||||
|
char *tempdir;
|
||||||
|
char linkdir[128];
|
||||||
|
ssize_t i;
|
||||||
|
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
|
||||||
|
strlen(searchbusid)) != 0))
|
||||||
|
return FTW_CONTINUE;
|
||||||
|
/*
|
||||||
|
* ensure that the found entry is a busid and not a
|
||||||
|
* subchannel ID
|
||||||
|
* for large systems subchannel IDs may look like busids
|
||||||
|
*/
|
||||||
|
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
|
||||||
|
return -1;
|
||||||
|
i = readlink(tempdir, linkdir, 128);
|
||||||
|
free(tempdir);
|
||||||
|
if ((i < 0) || (i >= 128))
|
||||||
|
return -1;
|
||||||
|
/* append '\0' because readlink returns non zero terminated string */
|
||||||
|
tempdir[i+1] = '\0';
|
||||||
|
if (strstr(linkdir, "dasd") == NULL)
|
||||||
|
return FTW_CONTINUE;
|
||||||
|
free(busiddir);
|
||||||
|
busiddir = strdup(fpath);
|
||||||
|
if (busiddir == NULL)
|
||||||
|
return -1;
|
||||||
|
return FTW_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dinfo_find_entry(const char *dir, const char *searchstring,
|
||||||
|
char type, char **result)
|
||||||
|
{
|
||||||
|
DIR *directory = NULL;
|
||||||
|
struct dirent *dir_entry = NULL;
|
||||||
|
directory = opendir(dir);
|
||||||
|
if (directory == NULL)
|
||||||
|
return -1;
|
||||||
|
while ((dir_entry = readdir(directory)) != NULL) {
|
||||||
|
/* compare if the found entry has exactly the same name
|
||||||
|
and type as searched */
|
||||||
|
if ((strncmp(dir_entry->d_name, searchstring,
|
||||||
|
strlen(searchstring)) == 0)
|
||||||
|
&& (dir_entry->d_type & type)) {
|
||||||
|
*result = strdup(dir_entry->d_name);
|
||||||
|
if (*result == NULL)
|
||||||
|
goto out;
|
||||||
|
closedir(directory);
|
||||||
|
return 0; /* found */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
closedir(directory);
|
||||||
|
return -1; /* nothing found or error */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
|
||||||
|
{
|
||||||
|
int flags = FTW_PHYS; /* do not follow links */
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
char *tempdir = NULL;
|
||||||
|
char *result = NULL;
|
||||||
|
char *sysfsdir = "/sys/devices/";
|
||||||
|
|
||||||
|
/* dinfo_is_devnode needs to know the busid */
|
||||||
|
searchbusid = busid;
|
||||||
|
if (nftw(sysfsdir, dinfo_is_busiddir, 200, flags) != FTW_STOP)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* new sysfs: busid directory contains a directory 'block'
|
||||||
|
* which contains a directory 'dasdXXX'
|
||||||
|
*/
|
||||||
|
rc = dinfo_find_entry(busiddir, "block", DT_DIR, &result);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (asprintf(&tempdir, "%s/%s/", busiddir, result) < 0) {
|
||||||
|
rc = -1;
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
rc = dinfo_find_entry(tempdir, "dasd", DT_DIR, blkdev);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* old sysfs: entry for busiddir contain a link
|
||||||
|
* 'block:dasdXXX'
|
||||||
|
*/
|
||||||
|
rc = dinfo_find_entry(busiddir, "block:", DT_LNK, &result);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out2;
|
||||||
|
*blkdev = strdup(strchr(result, ':') + 1);
|
||||||
|
if (*blkdev == NULL)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(tempdir);
|
||||||
|
out2:
|
||||||
|
free(busiddir);
|
||||||
|
free(result);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||||
|
{
|
||||||
|
struct stat stat_buffer;
|
||||||
|
char stat_dev[READCHUNK];
|
||||||
|
char sys_dev_path[READCHUNK];
|
||||||
|
char *readbuf;
|
||||||
|
DIR *directory = NULL;
|
||||||
|
struct dirent *dir_entry = NULL;
|
||||||
|
FILE *block_dev;
|
||||||
|
int readbuflen = READCHUNK;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
if (stat(devnode, &stat_buffer) != 0) {
|
||||||
|
printf("Error: could not stat %s\n", devnode);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
|
||||||
|
minor(stat_buffer.st_rdev));
|
||||||
|
|
||||||
|
if ((directory = opendir("/sys/block/")) == NULL) {
|
||||||
|
printf("Error: could not open directory /sys/block\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((readbuf = dinfo_malloc(readbuflen)) == NULL) {
|
||||||
|
printf("Error: Not enough memory to allocate readbuffer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((dir_entry = readdir(directory)) != NULL) {
|
||||||
|
sprintf(sys_dev_path, "/sys/block/%s/dev", dir_entry->d_name);
|
||||||
|
|
||||||
|
if ((block_dev = fopen(sys_dev_path,"r")) == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
while (fgets(readbuf + offset, READCHUNK, block_dev) &&
|
||||||
|
readbuf[strlen(readbuf)-1] != '\n' ) {
|
||||||
|
offset += READCHUNK-1;
|
||||||
|
if ( offset+READCHUNK-1 >= readbuflen ) {
|
||||||
|
readbuf = realloc(readbuf,
|
||||||
|
readbuflen + READCHUNK-1);
|
||||||
|
readbuflen += READCHUNK-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(block_dev);
|
||||||
|
|
||||||
|
if (strncmp(stat_dev, readbuf,
|
||||||
|
MAX(strlen(stat_dev), strlen(readbuf)-1)) == 0) {
|
||||||
|
sprintf(*uidfile,"/sys/block/%s/device/uid",
|
||||||
|
dir_entry->d_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(directory);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
struct utsname uname_buf;
|
||||||
|
int version, release;
|
||||||
|
char *uidfile = NULL;
|
||||||
|
char *device = NULL;
|
||||||
|
char *readbuf = NULL;
|
||||||
|
int readbuflen = READCHUNK;
|
||||||
|
dev_t dev;
|
||||||
|
int export = 0;
|
||||||
|
int c;
|
||||||
|
int print_uid = 0;
|
||||||
|
int print_extended_uid = 0;
|
||||||
|
int print_vlabel = 0;
|
||||||
|
char *blockdev = NULL;
|
||||||
|
char *busid = NULL;
|
||||||
|
char *devnode = NULL;
|
||||||
|
struct volume_label vlabel;
|
||||||
|
char *srchuid;
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
util_prg_init(&prg);
|
||||||
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
c = util_opt_getopt_long(argc, argv);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'a':
|
||||||
|
print_uid = 1;
|
||||||
|
print_vlabel = 1;
|
||||||
|
print_extended_uid = 1;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
print_uid = 1;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
print_extended_uid = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
print_vlabel = 1;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
busid=strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
blockdev=strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
devnode=strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
export = 1;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
util_prg_print_help();
|
||||||
|
util_opt_print_help();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'v':
|
||||||
|
util_prg_print_version();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Try 'dasdinfo --help' for more "
|
||||||
|
"information.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uname(&uname_buf);
|
||||||
|
sscanf(uname_buf.release, "%d.%d", &version,&release);
|
||||||
|
if (strcmp(uname_buf.sysname,"Linux") ||
|
||||||
|
version < 2 || (version == 2 && release < 6)) {
|
||||||
|
printf("%s %d.%d is not supported\n", uname_buf.sysname,
|
||||||
|
version,release);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!busid && !blockdev && !devnode) {
|
||||||
|
printf("Error: please specify a device using either -b, -i "
|
||||||
|
"or -d\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) {
|
||||||
|
printf("Error: please specify device only once, either -b, -i "
|
||||||
|
"or -d\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!print_uid && !print_extended_uid && !print_vlabel) {
|
||||||
|
printf("Error: no action specified (e.g. -u)\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((readbuf = dinfo_malloc(readbuflen)) == NULL) ||
|
||||||
|
((uidfile = dinfo_malloc(readbuflen)) == NULL))
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
/* try to read the uid attribute */
|
||||||
|
if (busid) {
|
||||||
|
sprintf(uidfile,"/sys/bus/ccw/devices/%s/uid", busid);
|
||||||
|
} else if (blockdev) {
|
||||||
|
sprintf(uidfile,"/sys/block/%s/device/uid", blockdev);
|
||||||
|
} else if (devnode) {
|
||||||
|
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (export) {
|
||||||
|
printf("ID_BUS=ccw\n");
|
||||||
|
printf("ID_TYPE=disk\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_uid) {
|
||||||
|
if (dinfo_read_dasd_uid(uidfile, readbuf, readbuflen) == 0) {
|
||||||
|
/* look for the 4th '.' and cut there */
|
||||||
|
srchuid = readbuf - 1;
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
srchuid = index(srchuid + 1, '.');
|
||||||
|
if (!srchuid)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (srchuid) {
|
||||||
|
srchuid[0] = '\n';
|
||||||
|
srchuid[1] = 0;
|
||||||
|
}
|
||||||
|
if (export) {
|
||||||
|
printf("ID_UID=%s",readbuf);
|
||||||
|
} else
|
||||||
|
printf("%s",readbuf);
|
||||||
|
if (!print_vlabel && !print_extended_uid)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_extended_uid) {
|
||||||
|
if (dinfo_read_dasd_uid(uidfile, readbuf, readbuflen) == 0) {
|
||||||
|
if (export) {
|
||||||
|
printf("ID_XUID=%s",readbuf);
|
||||||
|
} else
|
||||||
|
printf("%s",readbuf);
|
||||||
|
if (!print_vlabel)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* there is no uid, try to read the volume serial */
|
||||||
|
if (busid) {
|
||||||
|
char *blockdev_name = NULL;
|
||||||
|
|
||||||
|
if (dinfo_get_blockdev_from_busid(busid, &blockdev_name) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (dinfo_get_dev_from_blockdev(blockdev_name, &dev) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (dinfo_create_devnode(dev, &device) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
free(blockdev_name);
|
||||||
|
|
||||||
|
} else if (blockdev) {
|
||||||
|
if (dinfo_get_dev_from_blockdev(blockdev, &dev) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (dinfo_create_devnode(dev, &device) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
} else if (devnode) {
|
||||||
|
if ((device = dinfo_malloc(readbuflen)) == NULL)
|
||||||
|
exit(1);
|
||||||
|
strcpy(device, devnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dinfo_read_dasd_vlabel(device, &vlabel, readbuf) == 0) {
|
||||||
|
if (export) {
|
||||||
|
printf("ID_SERIAL=%s\n",readbuf);
|
||||||
|
} else
|
||||||
|
printf("%s\n", readbuf);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
printf("Error: could not read unique DASD ID\n");
|
||||||
|
rc = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (device && (busid || blockdev))
|
||||||
|
dinfo_free_devnode(device);
|
||||||
|
|
||||||
|
free(uidfile);
|
||||||
|
free(device);
|
||||||
|
free(readbuf);
|
||||||
|
|
||||||
|
exit(rc);
|
||||||
|
}
|
||||||
24
dasdview/Makefile
Normal file
24
dasdview/Makefile
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
ALL_CPPFLAGS += -DSYSFS
|
||||||
|
|
||||||
|
all: dasdview
|
||||||
|
|
||||||
|
libs = $(rootdir)/libdasd/libdasd.a \
|
||||||
|
$(rootdir)/libzds/libzds.a \
|
||||||
|
$(rootdir)/libvtoc/libvtoc.a \
|
||||||
|
$(rootdir)/libu2s/libu2s.a \
|
||||||
|
$(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
dasdview: dasdview.o $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdview $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdview.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ dasdview core
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
218
dasdview/dasdview.8
Normal file
218
dasdview/dasdview.8
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH DASDVIEW 8 "Apr 2006" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
dasdview \- Display DASD and VTOC information and dump the content of a DASD
|
||||||
|
to the console.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\fBdasdview\fR [-h] [-v]
|
||||||
|
.br
|
||||||
|
[-b \fIbegin\fR] [-s \fIsize\fR] [-1|-2]
|
||||||
|
.br
|
||||||
|
[-i] [-x] [-j] [-c]
|
||||||
|
.br
|
||||||
|
[-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}]
|
||||||
|
.br
|
||||||
|
\fIdevice\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\fBdasdview\fR prints you some useful information of your disks to the console.
|
||||||
|
You can display a disk dump by specifying start point and offset and you can
|
||||||
|
print the volume label and VTOC entries.
|
||||||
|
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
|
||||||
|
Any device node created by udev for kernel 2.6 can be used
|
||||||
|
(e.g. '/dev/dasd/0.0.b100/disc').
|
||||||
|
|
||||||
|
DASD devices in raw_track_access mode are supported and detected
|
||||||
|
automatically. When in raw_track_access mode, the same basic
|
||||||
|
functions are available as in the regular mode, but the output may
|
||||||
|
have a slightly different layout:
|
||||||
|
.IP \(bu 2
|
||||||
|
The disk dump functions (\fB-b\fR and \fB-s\fR) print the count,
|
||||||
|
key and data information for the whole track, and not just the
|
||||||
|
contents of the data areas.
|
||||||
|
.IP \(bu 2
|
||||||
|
The VTOC listing (\fB-t\fR) print all specified DSCBs in the same
|
||||||
|
format as in the regular mode, but in the sequence as they appear in
|
||||||
|
the VTOC. The \fB-t info\fR overview contains more details for each
|
||||||
|
data set than in the regular mode, to support the larger variety of
|
||||||
|
data set layouts.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB-h\fR or \fB--help\fR
|
||||||
|
Print usage and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-v\fR or \fB--version\fR
|
||||||
|
Print version number and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-b\fR \fIbegin\fR or \fB--begin=\fR\fIbegin\fR
|
||||||
|
Print a disk dump to the console, starting with \fIbegin\fR. The content of
|
||||||
|
the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text.
|
||||||
|
If no size is specified dasdview will take the default size. The variable
|
||||||
|
\fIbegin\fR can be specified in one of the following ways:
|
||||||
|
.br
|
||||||
|
|
||||||
|
begin[k|m|b|t|c]
|
||||||
|
|
||||||
|
.br
|
||||||
|
The default for \fIbegin\fR is \fI0\fR.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fBNote 1:\fR dasdview will show you the content of your disk using the DASD
|
||||||
|
driver. If this driver decides to hide or add some parts of the disk, you have
|
||||||
|
to live with it. This happens for example with the first two tracks of a
|
||||||
|
cdl-formatted disk. In this case the DASD driver fills up shorter blocks with
|
||||||
|
zeros to have a constant blocksize. And all applications, including dasdview,
|
||||||
|
believe it.
|
||||||
|
.br
|
||||||
|
\fBNote 2:\fR In raw_track_access mode \fIbegin\fR must be aligned to
|
||||||
|
track boundaries. A simple way to do that is to specify a track or
|
||||||
|
cylinder as starting point.
|
||||||
|
.br
|
||||||
|
|
||||||
|
examples:
|
||||||
|
.br
|
||||||
|
-b 32 --> start printing at Byte 32
|
||||||
|
.br
|
||||||
|
-b 32k --> start printing at kByte 32
|
||||||
|
.br
|
||||||
|
-b 32m --> start printing at MByte 32
|
||||||
|
.br
|
||||||
|
-b 32b --> start printing at block 32
|
||||||
|
.br
|
||||||
|
-b 32t --> start printing at track 32
|
||||||
|
.br
|
||||||
|
-b 32c --> start printing at cylinder 32
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-s\fR \fIsize\fR or \fB--size=\fR\fIsize\fR
|
||||||
|
Print a disk dump to the console, starting with \fIbegin\fR, specified with
|
||||||
|
the \fB-b\fR option and size \fIsize\fR. The content of the disk will be
|
||||||
|
displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no start
|
||||||
|
value is specified dasdview will take the default start value. The variable
|
||||||
|
\fIsize\fR can be specified in one of the following ways:
|
||||||
|
.br
|
||||||
|
|
||||||
|
size[k|m|b|t|c]
|
||||||
|
|
||||||
|
.br
|
||||||
|
\fBNote:\fR In raw_track_access mode \fIsize\fR must be a multiple of
|
||||||
|
one track. A simple way to do that is to specify the size in tracks or
|
||||||
|
cylinders.
|
||||||
|
|
||||||
|
.br
|
||||||
|
The default for \fIsize\fR is \fI128\fR in regular mode and \fI1t\fR
|
||||||
|
in raw_track_access mode.
|
||||||
|
.br
|
||||||
|
|
||||||
|
examples:
|
||||||
|
.br
|
||||||
|
-s 16 --> use a 16 Byte size
|
||||||
|
.br
|
||||||
|
-s 16k --> use a 16 kByte size
|
||||||
|
.br
|
||||||
|
-s 16m --> use a 16 MByte size
|
||||||
|
.br
|
||||||
|
-s 16b --> use a 16 block size
|
||||||
|
.br
|
||||||
|
-s 16t --> use a 16 track size
|
||||||
|
.br
|
||||||
|
-s 16c --> use a 16 cylinder size
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-1\fR
|
||||||
|
This option tells dasdview to print the disk dump using format 1. This means
|
||||||
|
you will get 16 Bytes per line in hex, ascii and ebcdic. There is no line
|
||||||
|
number.
|
||||||
|
.br
|
||||||
|
The \fB-1\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
|
||||||
|
options.
|
||||||
|
.br
|
||||||
|
This is the default.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-2\fR
|
||||||
|
This option tells dasdview to print the disk dump using format 2. This means
|
||||||
|
you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line
|
||||||
|
number and a decimal and hexadecimal byte count will be printed.
|
||||||
|
.br
|
||||||
|
The \fB-2\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
|
||||||
|
options. In raw_track_access mode this format is not supported and the
|
||||||
|
option will be ignored.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-i\fR or \fB--info\fR
|
||||||
|
Print some useful information (e.g. device node/number/type or geometry data).
|
||||||
|
When running dasdview on a kernel 2.6 based distribution the busid
|
||||||
|
is printed instead of the device number.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-x\fR or \fB--extended\fR
|
||||||
|
Print some more DASD information (e.g. open count, subchannel identifier).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-j\fR or \fB--volser\fR
|
||||||
|
Print volume serial number (volume identifier).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-l\fR or \fB--label\fR
|
||||||
|
Print the volume label.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-c\fR or \fB--characteristic\fR
|
||||||
|
Print some information about the device e.g. if it is encrypted.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-t\fR \fIspec\fR or \fB--vtoc=\fR\fIspec\fR
|
||||||
|
Print the VTOC (table of content) or single VTOC entries to the console.
|
||||||
|
\fIspec\fR can be one of the following strings:
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIinfo\fR:
|
||||||
|
.br
|
||||||
|
Gives you a VTOC overview. You will see what other S/390 or zSeries operating
|
||||||
|
systems would see (e.g. data set names and sizes).
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf1\fR:
|
||||||
|
.br
|
||||||
|
Print the content of all format 1 DSCBs.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf3\fR:
|
||||||
|
.br
|
||||||
|
Print the content of all format 3 DSCBs.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf4\fR:
|
||||||
|
.br
|
||||||
|
Print the content of the format 4 DSCB.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf5\fR:
|
||||||
|
.br
|
||||||
|
Print the content of the format 5 DSCB.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf7\fR:
|
||||||
|
.br
|
||||||
|
Print the content of the format 7 DSCB.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf8\fR:
|
||||||
|
.br
|
||||||
|
Print the content of all format 8 DSCBs.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIf9\fR:
|
||||||
|
.br
|
||||||
|
Print the content of all format 9 DSCBs.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fIall\fR:
|
||||||
|
.br
|
||||||
|
Print the content of all DSCBs.
|
||||||
2470
dasdview/dasdview.c
Normal file
2470
dasdview/dasdview.c
Normal file
File diff suppressed because it is too large
Load Diff
269
dasdview/dasdview.h
Normal file
269
dasdview/dasdview.h
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* dasdview - Display DASD and VTOC information or dump the contents of a DASD
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2002, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DASDVIEW_H
|
||||||
|
#define DASDVIEW_H
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include "lib/u2s.h"
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* SECTION: Definitions needed for DASD-API (see dasd.h)
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#define DASD_IOCTL_LETTER 'D'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct dasd_information_t
|
||||||
|
* represents any data about the device, which is visible to userspace.
|
||||||
|
* including foramt and features.
|
||||||
|
*/
|
||||||
|
typedef struct dasd_information_t {
|
||||||
|
unsigned int devno; /* S/390 devno */
|
||||||
|
unsigned int real_devno; /* for aliases */
|
||||||
|
unsigned int schid; /* S/390 subchannel identifier */
|
||||||
|
unsigned int cu_type : 16; /* from SenseID */
|
||||||
|
unsigned int cu_model : 8; /* from SenseID */
|
||||||
|
unsigned int dev_type : 16; /* from SenseID */
|
||||||
|
unsigned int dev_model : 8; /* from SenseID */
|
||||||
|
unsigned int open_count;
|
||||||
|
unsigned int req_queue_len;
|
||||||
|
unsigned int chanq_len; /* length of chanq */
|
||||||
|
char type[4]; /* from discipline.name, 'none' for unknown */
|
||||||
|
unsigned int status; /* current device level */
|
||||||
|
unsigned int label_block; /* where to find the VOLSER */
|
||||||
|
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
|
||||||
|
unsigned int characteristics_size;
|
||||||
|
unsigned int confdata_size;
|
||||||
|
char characteristics[64]; /* from read_device_characteristics */
|
||||||
|
char configuration_data[256]; /* from read_configuration_data */
|
||||||
|
} dasd_information_t;
|
||||||
|
|
||||||
|
typedef struct dasd_information2_t {
|
||||||
|
unsigned int devno; /* S/390 devno */
|
||||||
|
unsigned int real_devno; /* for aliases */
|
||||||
|
unsigned int schid; /* S/390 subchannel identifier */
|
||||||
|
unsigned int cu_type : 16; /* from SenseID */
|
||||||
|
unsigned int cu_model : 8; /* from SenseID */
|
||||||
|
unsigned int dev_type : 16; /* from SenseID */
|
||||||
|
unsigned int dev_model : 8; /* from SenseID */
|
||||||
|
unsigned int open_count;
|
||||||
|
unsigned int req_queue_len;
|
||||||
|
unsigned int chanq_len; /* length of chanq */
|
||||||
|
char type[4]; /* from discipline.name, 'none' for unknown */
|
||||||
|
unsigned int status; /* current device level */
|
||||||
|
unsigned int label_block; /* where to find the VOLSER */
|
||||||
|
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
|
||||||
|
unsigned int characteristics_size;
|
||||||
|
unsigned int confdata_size;
|
||||||
|
unsigned char characteristics[64];/*from read_device_characteristics */
|
||||||
|
unsigned char configuration_data[256];/*from read_configuration_data */
|
||||||
|
unsigned int format; /* format info like formatted/cdl/ldl/... */
|
||||||
|
unsigned int features; /* dasd features like 'ro',... */
|
||||||
|
unsigned int reserved0; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved1; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved2; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved3; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved4; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved5; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved6; /* reserved for further use ,... */
|
||||||
|
unsigned int reserved7; /* reserved for further use ,... */
|
||||||
|
} dasd_information2_t;
|
||||||
|
|
||||||
|
struct dasd_eckd_characteristics {
|
||||||
|
unsigned short cu_type;
|
||||||
|
struct {
|
||||||
|
unsigned char support:2;
|
||||||
|
unsigned char async:1;
|
||||||
|
unsigned char reserved:1;
|
||||||
|
unsigned char cache_info:1;
|
||||||
|
unsigned char model:3;
|
||||||
|
} __attribute__ ((packed)) cu_model;
|
||||||
|
unsigned short dev_type;
|
||||||
|
unsigned char dev_model;
|
||||||
|
struct {
|
||||||
|
unsigned char mult_burst:1;
|
||||||
|
unsigned char RT_in_LR:1;
|
||||||
|
unsigned char reserved1:1;
|
||||||
|
unsigned char RD_IN_LR:1;
|
||||||
|
unsigned char reserved2:4;
|
||||||
|
unsigned char reserved3:8;
|
||||||
|
unsigned char defect_wr:1;
|
||||||
|
unsigned char XRC_supported:1;
|
||||||
|
unsigned char reserved4:1;
|
||||||
|
unsigned char striping:1;
|
||||||
|
unsigned char reserved5:4;
|
||||||
|
unsigned char cfw:1;
|
||||||
|
unsigned char reserved6:2;
|
||||||
|
unsigned char cache:1;
|
||||||
|
unsigned char dual_copy:1;
|
||||||
|
unsigned char dfw:1;
|
||||||
|
unsigned char reset_alleg:1;
|
||||||
|
unsigned char sense_down:1;
|
||||||
|
} __attribute__ ((packed)) facilities;
|
||||||
|
unsigned char dev_class;
|
||||||
|
unsigned char unit_type;
|
||||||
|
unsigned short no_cyl;
|
||||||
|
unsigned short trk_per_cyl;
|
||||||
|
unsigned char sec_per_trk;
|
||||||
|
unsigned char byte_per_track[3];
|
||||||
|
unsigned short home_bytes;
|
||||||
|
unsigned char formula;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
unsigned char f1;
|
||||||
|
unsigned short f2;
|
||||||
|
unsigned short f3;
|
||||||
|
} __attribute__ ((packed)) f_0x01;
|
||||||
|
struct {
|
||||||
|
unsigned char f1;
|
||||||
|
unsigned char f2;
|
||||||
|
unsigned char f3;
|
||||||
|
unsigned char f4;
|
||||||
|
unsigned char f5;
|
||||||
|
} __attribute__ ((packed)) f_0x02;
|
||||||
|
} __attribute__ ((packed)) factors;
|
||||||
|
unsigned short first_alt_trk;
|
||||||
|
unsigned short no_alt_trk;
|
||||||
|
unsigned short first_dia_trk;
|
||||||
|
unsigned short no_dia_trk;
|
||||||
|
unsigned short first_sup_trk;
|
||||||
|
unsigned short no_sup_trk;
|
||||||
|
unsigned char MDR_ID;
|
||||||
|
unsigned char OBR_ID;
|
||||||
|
unsigned char director;
|
||||||
|
unsigned char rd_trk_set;
|
||||||
|
unsigned short max_rec_zero;
|
||||||
|
unsigned char reserved1;
|
||||||
|
unsigned char RWANY_in_LR;
|
||||||
|
unsigned char factor6;
|
||||||
|
unsigned char factor7;
|
||||||
|
unsigned char factor8;
|
||||||
|
unsigned char reserved2[3];
|
||||||
|
unsigned char reserved3[6];
|
||||||
|
unsigned int long_no_cyl;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* values to be used for dasd_information2_t.format
|
||||||
|
* 0x00: NOT formatted
|
||||||
|
* 0x01: Linux disc layout
|
||||||
|
* 0x02: Common disc layout
|
||||||
|
*/
|
||||||
|
#define DASD_FORMAT_NONE 0
|
||||||
|
#define DASD_FORMAT_LDL 1
|
||||||
|
#define DASD_FORMAT_CDL 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* values to be used for dasd_information2_t.features
|
||||||
|
* 0x00: default features
|
||||||
|
* 0x01: readonly (ro)
|
||||||
|
* 0x02: use diag discipline (diag)
|
||||||
|
*/
|
||||||
|
#define DASD_FEATURE_DEFAULT 0
|
||||||
|
#define DASD_FEATURE_READONLY 1
|
||||||
|
#define DASD_FEATURE_USEDIAG 2
|
||||||
|
|
||||||
|
/* Get information on a dasd device (enhanced) */
|
||||||
|
#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t)
|
||||||
|
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t)
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h)
|
||||||
|
*******************************************************************************/
|
||||||
|
/* get block device sector size */
|
||||||
|
#define BLKSSZGET _IO(0x12,104)
|
||||||
|
/* return device size in bytes (u64 *arg) */
|
||||||
|
#define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
||||||
|
|
||||||
|
/* get device geometry */
|
||||||
|
#define HDIO_GETGEO 0x0301
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* SECTION: DASDVIEW internal types
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#define LINE_LENGTH 80
|
||||||
|
#define DASDVIEW_ERROR "dasdview:"
|
||||||
|
#define DEFAULT_BEGIN 0
|
||||||
|
#define DEFAULT_SIZE 128
|
||||||
|
#define NO_PART_LABELS 8 /* for partition related labels (f1,f8 and f9) */
|
||||||
|
#define SEEK_STEP 4194304LL
|
||||||
|
#define DUMP_STRING_SIZE 1024LL
|
||||||
|
|
||||||
|
#define ERROR_STRING_SIZE 1024
|
||||||
|
static char error_str[ERROR_STRING_SIZE];
|
||||||
|
|
||||||
|
enum dasdview_failure {
|
||||||
|
open_error,
|
||||||
|
seek_error,
|
||||||
|
read_error,
|
||||||
|
ioctl_error,
|
||||||
|
usage_error,
|
||||||
|
disk_layout,
|
||||||
|
vtoc_error
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct dasdview_info
|
||||||
|
{
|
||||||
|
char device[PATH_MAX];
|
||||||
|
dasd_information2_t dasd_info;
|
||||||
|
int dasd_info_version;
|
||||||
|
int blksize;
|
||||||
|
struct hd_geometry geo;
|
||||||
|
u_int32_t hw_cylinders;
|
||||||
|
|
||||||
|
unsigned long long begin;
|
||||||
|
unsigned long long size;
|
||||||
|
int format1;
|
||||||
|
int format2;
|
||||||
|
|
||||||
|
int action_specified;
|
||||||
|
int begin_specified;
|
||||||
|
int size_specified;
|
||||||
|
int characteristic_specified;
|
||||||
|
int device_id;
|
||||||
|
int general_info;
|
||||||
|
int extended_info;
|
||||||
|
int volser;
|
||||||
|
int vtoc;
|
||||||
|
int vtoc_info;
|
||||||
|
int vtoc_f1;
|
||||||
|
int vtoc_f3;
|
||||||
|
int vtoc_f4;
|
||||||
|
int vtoc_f5;
|
||||||
|
int vtoc_f7;
|
||||||
|
int vtoc_f8;
|
||||||
|
int vtoc_f9;
|
||||||
|
int vtoc_all;
|
||||||
|
int vlabel_info;
|
||||||
|
|
||||||
|
format1_label_t f1[NO_PART_LABELS];
|
||||||
|
format4_label_t f4;
|
||||||
|
format5_label_t f5;
|
||||||
|
format7_label_t f7;
|
||||||
|
format1_label_t f8[NO_PART_LABELS];
|
||||||
|
format9_label_t f9[NO_PART_LABELS];
|
||||||
|
int f1c;
|
||||||
|
int f4c;
|
||||||
|
int f5c;
|
||||||
|
int f7c;
|
||||||
|
int f8c;
|
||||||
|
int f9c;
|
||||||
|
|
||||||
|
char busid[U2S_BUS_ID_SIZE];
|
||||||
|
int busid_valid;
|
||||||
|
int raw_track_access;
|
||||||
|
struct zdsroot *zdsroot;
|
||||||
|
struct raw_vtoc *rawvtoc;
|
||||||
|
struct dasd *dasd;
|
||||||
|
|
||||||
|
} dasdview_info_t;
|
||||||
|
|
||||||
|
#endif /* DASDVIEW_H */
|
||||||
12
dump2tar/Makefile
Normal file
12
dump2tar/Makefile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Common definitions
|
||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(MAKE) -C src
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(MAKE) -C src install
|
||||||
|
$(MAKE) -C man install
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C src clean
|
||||||
50
dump2tar/include/buffer.h
Normal file
50
dump2tar/include/buffer.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Data buffering functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BUFFER_H
|
||||||
|
#define BUFFER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Buffers for building tar file entries */
|
||||||
|
struct buffer {
|
||||||
|
size_t total; /* Total number of bytes in buffer */
|
||||||
|
size_t off; /* Current offset to next free byte in memory buffer */
|
||||||
|
size_t size; /* Memory buffer size */
|
||||||
|
char *addr; /* Memory buffer address */
|
||||||
|
bool fd_open; /* Has fd been openend yet? */
|
||||||
|
FILE *file; /* FILE * of file containing previous buffer data */
|
||||||
|
int fd; /* Handle of file containing previous buffer data */
|
||||||
|
};
|
||||||
|
|
||||||
|
void buffer_init(struct buffer *buffer, size_t size);
|
||||||
|
struct buffer *buffer_alloc(size_t size);
|
||||||
|
void buffer_reset(struct buffer *buffer);
|
||||||
|
void buffer_close(struct buffer *buffer);
|
||||||
|
void buffer_free(struct buffer *buffer, bool dyn);
|
||||||
|
int buffer_open(struct buffer *buffer);
|
||||||
|
int buffer_flush(struct buffer *buffer);
|
||||||
|
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
|
||||||
|
size_t max_buffer_size);
|
||||||
|
int buffer_truncate(struct buffer *buffer, size_t len);
|
||||||
|
|
||||||
|
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
|
||||||
|
bool usefile, size_t max_buffer_size);
|
||||||
|
int buffer_add_data(struct buffer *buffer, char *addr, size_t len,
|
||||||
|
bool usefile, size_t max_buffer_size);
|
||||||
|
|
||||||
|
typedef int (*buffer_cb_t)(void *data, void *addr, size_t len);
|
||||||
|
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data);
|
||||||
|
void buffer_print(struct buffer *buffer);
|
||||||
|
|
||||||
|
#endif /* BUFFER_H */
|
||||||
29
dump2tar/include/dref.h
Normal file
29
dump2tar/include/dref.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Reference counting for directory handles
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DREF_H
|
||||||
|
#define DREF_H
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Multiple jobs may refer to an open DIR * - need reference counting */
|
||||||
|
struct dref {
|
||||||
|
DIR *dd;
|
||||||
|
int dirfd;
|
||||||
|
unsigned int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dref *dref_create(const char *dirname);
|
||||||
|
struct dref *dref_get(struct dref *dref);
|
||||||
|
void dref_put(struct dref *dref);
|
||||||
|
|
||||||
|
#endif /* DREF_H */
|
||||||
63
dump2tar/include/dump.h
Normal file
63
dump2tar/include/dump.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Main dump logic
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DUMP_H
|
||||||
|
#define DUMP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "strarray.h"
|
||||||
|
|
||||||
|
#define NUM_EXCLUDE_TYPES 7
|
||||||
|
|
||||||
|
struct dump_spec {
|
||||||
|
char *inname;
|
||||||
|
char *outname;
|
||||||
|
bool is_cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dump_opts {
|
||||||
|
bool add_cmd_status;
|
||||||
|
bool append;
|
||||||
|
bool dereference;
|
||||||
|
bool exclude_type[NUM_EXCLUDE_TYPES];
|
||||||
|
bool gzip;
|
||||||
|
bool ignore_failed_read;
|
||||||
|
bool no_eof;
|
||||||
|
bool quiet;
|
||||||
|
bool recursive;
|
||||||
|
bool threaded;
|
||||||
|
bool verbose;
|
||||||
|
const char *output_file;
|
||||||
|
int file_timeout;
|
||||||
|
int timeout;
|
||||||
|
long jobs;
|
||||||
|
long jobs_per_cpu;
|
||||||
|
size_t file_max_size;
|
||||||
|
size_t max_buffer_size;
|
||||||
|
size_t max_size;
|
||||||
|
size_t read_chunk_size;
|
||||||
|
struct strarray exclude;
|
||||||
|
struct dump_spec *specs;
|
||||||
|
unsigned int num_specs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dump_opts *dump_opts_new(void);
|
||||||
|
int dump_opts_set_type_excluded(struct dump_opts *opts, char c);
|
||||||
|
void dump_opts_add_spec(struct dump_opts *opts, char *inname, char *outname,
|
||||||
|
bool is_cmd);
|
||||||
|
void dump_opts_free(struct dump_opts *opts);
|
||||||
|
|
||||||
|
int dump_to_tar(struct dump_opts *opts);
|
||||||
|
|
||||||
|
#endif /* DUMP_H */
|
||||||
23
dump2tar/include/global.h
Normal file
23
dump2tar/include/global.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Global variables
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GLOBAL_H
|
||||||
|
#define GLOBAL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern bool global_threaded;
|
||||||
|
extern bool global_debug;
|
||||||
|
extern bool global_verbose;
|
||||||
|
extern bool global_quiet;
|
||||||
|
extern bool global_timestamps;
|
||||||
|
|
||||||
|
#endif /* GLOBAL_H */
|
||||||
26
dump2tar/include/idcache.h
Normal file
26
dump2tar/include/idcache.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Caches for user and group ID lookups
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IDCACHE_H
|
||||||
|
#define IDCACHE_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/* Buffer sizes for getpwuid_r and getgid_r calls (bytes) */
|
||||||
|
#define PWD_BUFFER_SIZE 4096
|
||||||
|
#define GRP_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
void uid_to_name(uid_t uid, char *name, size_t len);
|
||||||
|
void gid_to_name(gid_t gid, char *name, size_t len);
|
||||||
|
void idcache_cleanup(void);
|
||||||
|
|
||||||
|
#endif /* IDCACHE_H */
|
||||||
101
dump2tar/include/misc.h
Normal file
101
dump2tar/include/misc.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Helper functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MISC_H
|
||||||
|
#define MISC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MSG_LEN 256
|
||||||
|
|
||||||
|
#define DBG(...) \
|
||||||
|
do { \
|
||||||
|
if (global_debug) \
|
||||||
|
debug(__FILE__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define mwarn(fmt, ...) _mwarn(true, (fmt), ##__VA_ARGS__)
|
||||||
|
#define mwarnx(fmt, ...) _mwarn(false, (fmt), ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/* Helper macro for constructing messages in variables */
|
||||||
|
#define HANDLE_RC(rc, max, off, label) \
|
||||||
|
do { \
|
||||||
|
if ((rc) > 0) \
|
||||||
|
(off) += (rc); \
|
||||||
|
if ((off) > (max)) \
|
||||||
|
goto label; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Program exit codes */
|
||||||
|
#define EXIT_OK 0
|
||||||
|
#define EXIT_RUNTIME 1
|
||||||
|
#define EXIT_USAGE 2
|
||||||
|
|
||||||
|
/* Number of nanoseconds in a second */
|
||||||
|
#define NSEC_PER_SEC 1000000000L
|
||||||
|
#define NSEC_PER_MSEC 1000000L
|
||||||
|
#define NSEC_PER_USEC 1000L
|
||||||
|
|
||||||
|
extern struct timespec main_start_ts;
|
||||||
|
struct dref;
|
||||||
|
|
||||||
|
int misc_write_data(int fd, char *addr, size_t len);
|
||||||
|
ssize_t misc_read_data(int fd, char *addr, size_t len);
|
||||||
|
void inc_timespec(struct timespec *ts, time_t sec, long nsec);
|
||||||
|
void set_timespec(struct timespec *ts, time_t sec, long nsec);
|
||||||
|
bool ts_before(struct timespec *a, struct timespec *b);
|
||||||
|
int snprintf_duration(char *buff, size_t len, struct timespec *start,
|
||||||
|
struct timespec *end);
|
||||||
|
char *get_threadname(void);
|
||||||
|
void debug(const char *file, unsigned long line, const char *format, ...);
|
||||||
|
void _mwarn(bool print_errno, const char *format, ...);
|
||||||
|
void verb(const char *format, ...);
|
||||||
|
void info(const char *format, ...);
|
||||||
|
#define mmalloc(len) util_zalloc(len)
|
||||||
|
#define mcalloc(n, len) util_zalloc((n) * (len))
|
||||||
|
#define mrealloc(ptr, len) util_realloc((ptr), (len))
|
||||||
|
#define mstrdup(str) util_strdup(str)
|
||||||
|
#define masprintf(fmt, ...) __masprintf(__func__, __FILE__, __LINE__, \
|
||||||
|
(fmt), ##__VA_ARGS__)
|
||||||
|
char *__masprintf(const char *func, const char *file, int line,
|
||||||
|
const char *fmt, ...);
|
||||||
|
#define set_threadname(fmt, ...) __set_threadname(__func__, __FILE__, \
|
||||||
|
__LINE__, (fmt), \
|
||||||
|
##__VA_ARGS__)
|
||||||
|
void __set_threadname(const char *func, const char *file, int line,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
void clear_threadname(void);
|
||||||
|
void chomp(char *str, char *c);
|
||||||
|
void lchomp(char *str, char *c);
|
||||||
|
void remove_double_slashes(char *str);
|
||||||
|
int stat_file(bool dereference, const char *abs, const char *rel,
|
||||||
|
struct dref *dref, struct stat *st);
|
||||||
|
void set_dummy_stat(struct stat *st);
|
||||||
|
bool starts_with(const char *str, const char *prefix);
|
||||||
|
bool ends_with(const char *str, const char *suffix);
|
||||||
|
|
||||||
|
int cmd_child(int fd, char *cmd);
|
||||||
|
int cmd_open(char *cmd, pid_t *pid_ptr);
|
||||||
|
int cmd_close(int fd, pid_t pid, int *status_ptr);
|
||||||
|
|
||||||
|
void misc_init(void);
|
||||||
|
void misc_cleanup(void);
|
||||||
|
void set_stdout_data(void);
|
||||||
|
|
||||||
|
#endif /* MISC_H */
|
||||||
26
dump2tar/include/strarray.h
Normal file
26
dump2tar/include/strarray.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Dynamically growing string arrays
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRARRAY_H
|
||||||
|
#define STRARRAY_H
|
||||||
|
|
||||||
|
/* A string array that can grow in size */
|
||||||
|
struct strarray {
|
||||||
|
unsigned int num;
|
||||||
|
char **str;
|
||||||
|
};
|
||||||
|
|
||||||
|
void free_strarray(struct strarray *array);
|
||||||
|
void add_str_to_strarray(struct strarray *array, const char *str);
|
||||||
|
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...);
|
||||||
|
int add_file_to_strarray(struct strarray *array, const char *filename);
|
||||||
|
|
||||||
|
#endif /* STRARRAY_H */
|
||||||
44
dump2tar/include/tar.h
Normal file
44
dump2tar/include/tar.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* TAR file generation
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TAR_H
|
||||||
|
#define TAR_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define TYPE_REGULAR '0'
|
||||||
|
#define TYPE_LINK '2'
|
||||||
|
#define TYPE_DIR '5'
|
||||||
|
|
||||||
|
#define TAR_BLOCKSIZE 512
|
||||||
|
|
||||||
|
struct buffer;
|
||||||
|
|
||||||
|
/* emit_cb_t - Callback used for emitting chunks of a byte stream
|
||||||
|
* @data: Arbitrary pointer passed via the @data parameter of the
|
||||||
|
* tar_emit_file_* functions
|
||||||
|
* @addr: Pointer to data
|
||||||
|
* @len: Size of data
|
||||||
|
* Return %0 on success. Returning non-zero will indicate failure and abort
|
||||||
|
* further data emission. */
|
||||||
|
typedef int (*emit_cb_t)(void *data, void *addr, size_t len);
|
||||||
|
|
||||||
|
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
|
||||||
|
struct stat *stat, char type,
|
||||||
|
struct buffer *content, emit_cb_t emit_cb,
|
||||||
|
void *data);
|
||||||
|
int tar_emit_file_from_data(char *filename, char *link, size_t len,
|
||||||
|
struct stat *stat, char type, void *addr,
|
||||||
|
emit_cb_t emit_cb, void *data);
|
||||||
|
|
||||||
|
#endif /* TAR_H */
|
||||||
12
dump2tar/man/Makefile
Normal file
12
dump2tar/man/Makefile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Common definitions
|
||||||
|
include ../../common.mak
|
||||||
|
|
||||||
|
all:
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
$(INSTALL) -m 644 -c dump2tar.1 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
|
clean:
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
458
dump2tar/man/dump2tar.1
Normal file
458
dump2tar/man/dump2tar.1
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.\" Macro for inserting an option description prologue.
|
||||||
|
.\" .OD <long> [<short>] [args]
|
||||||
|
.de OD
|
||||||
|
. ds args "
|
||||||
|
. if !'\\$3'' .as args \fI\\$3\fP
|
||||||
|
. if !'\\$4'' .as args \\$4
|
||||||
|
. if !'\\$5'' .as args \fI\\$5\fP
|
||||||
|
. if !'\\$6'' .as args \\$6
|
||||||
|
. if !'\\$7'' .as args \fI\\$7\fP
|
||||||
|
. PD 0
|
||||||
|
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
|
||||||
|
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
||||||
|
. PD
|
||||||
|
..
|
||||||
|
.\" Macro for inserting code line.
|
||||||
|
.\" .CL <text>
|
||||||
|
.de CL
|
||||||
|
. ds pfont \\n[.f]
|
||||||
|
. nh
|
||||||
|
. na
|
||||||
|
. ft CW
|
||||||
|
\\$*
|
||||||
|
. ft \\*[pfont]
|
||||||
|
. ad
|
||||||
|
. hy
|
||||||
|
. br
|
||||||
|
..
|
||||||
|
.\" Macro for inserting a man page reference.
|
||||||
|
.\" .MP man-page section [suffix]
|
||||||
|
.de MP
|
||||||
|
. nh
|
||||||
|
. na
|
||||||
|
. BR \\$1 (\\$2)\\$3
|
||||||
|
. ad
|
||||||
|
. hy
|
||||||
|
..
|
||||||
|
.
|
||||||
|
.TH "dump2tar" "1" "2016\-09\-02" "" ""
|
||||||
|
.
|
||||||
|
.SH "NAME"
|
||||||
|
dump2tar - Gather file contents and command output into a tar archive
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B "dump2tar "
|
||||||
|
.RI "[" "OPTIONS" "] " "SPECS"
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.B dump2tar
|
||||||
|
creates a tar archive from the contents of any files, including files of
|
||||||
|
unknown size.
|
||||||
|
|
||||||
|
Examples for files of unknown size are:
|
||||||
|
.IP \(bu 3
|
||||||
|
Named pipes (FIFOs)
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
Particular Linux kernel debugfs or sysfs files
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
Character or block devices
|
||||||
|
.PP
|
||||||
|
|
||||||
|
When adding such a file,
|
||||||
|
.B dump2tar
|
||||||
|
first reads all available data until an end-of-file indication is found. From
|
||||||
|
this data, it then creates a regular file entry in the resulting tar archive.
|
||||||
|
By default, symbolic links and directories are preserved in the archive in
|
||||||
|
their original form.
|
||||||
|
|
||||||
|
.B dump2tar
|
||||||
|
can also:
|
||||||
|
.IP \(bu 3
|
||||||
|
Add files under a different name
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
Run arbitrary commands and add the resulting command output as a
|
||||||
|
regular file
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "FILE SPECIFICATIONS"
|
||||||
|
.
|
||||||
|
This section describes the format of the
|
||||||
|
.I SPECS
|
||||||
|
argument mentioned in the command synopsis.
|
||||||
|
Use the following command line syntax to identify data sources and
|
||||||
|
to specify file names within the archive:
|
||||||
|
.PP
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.I "PATH"
|
||||||
|
Adds the contents of the file system subtree at file system location
|
||||||
|
.I PATH
|
||||||
|
(with possible exceptions described by options) in the archive under the same
|
||||||
|
file name as on the file system.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.IR "FILENAME" ":=" "PATH"
|
||||||
|
Adds the contents of the file at file system location
|
||||||
|
.I PATH
|
||||||
|
in the archive under the name specified by
|
||||||
|
.IR FILENAME .
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.IR "FILENAME" "|=" "CMDLINE"
|
||||||
|
Runs the command
|
||||||
|
.IR CMDLINE
|
||||||
|
and captures both the resulting standard output and standard error streams.
|
||||||
|
Adds the collected output as a regular file named
|
||||||
|
.I FILENAME
|
||||||
|
in the resulting archive. You can also include the resulting program exit code
|
||||||
|
by using option \-\-add\-cmd\-status.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
You can also specify "\-\-". All specifications that follow are interpreted as
|
||||||
|
simple file names. This is useful for archiving files that contain ":=" or "|=".
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "OUTPUT OPTIONS"
|
||||||
|
.
|
||||||
|
.OD "output\-file" "o" "TARFILE"
|
||||||
|
Writes the resulting tar archive to
|
||||||
|
.IR TARFILE .
|
||||||
|
An existing file at the specified file system location is overwritten.
|
||||||
|
|
||||||
|
If this option is omitted or if "\-" is specified for
|
||||||
|
.IR TARFILE ,
|
||||||
|
the archive is written to the standard output stream.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "gzip" "z" ""
|
||||||
|
Compresses the resulting tar archive using gzip.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "max\-size" "m" "VALUE"
|
||||||
|
Sets an upper size limit, in bytes, for the resulting archive. If this limit
|
||||||
|
is exceeded after adding a file, no further files are added.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "timeout" "t" "VALUE"
|
||||||
|
Sets an upper time limit, in seconds, for the archiving process. If this limit
|
||||||
|
is exceeded while adding a file, that file is truncated and no
|
||||||
|
further files are added.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "no-eof" "" ""
|
||||||
|
Does not write an end-of-file marker.
|
||||||
|
|
||||||
|
Use this option if you want to create an archive that can be extended by
|
||||||
|
appending additional tar archive data.
|
||||||
|
|
||||||
|
Note: Do not use this option for the final data to be added.
|
||||||
|
A valid tar archive requires a trailing end-of-file marker.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "append" "" ""
|
||||||
|
Appends data to the end of the archive.
|
||||||
|
|
||||||
|
Use this option to incrementally build a tar file by repeatedly calling
|
||||||
|
.BR dump2tar .
|
||||||
|
You must specify the \-\-no\-eof option for each but the final call of
|
||||||
|
.BR dump2tar .
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "add-cmd-status" "" ""
|
||||||
|
Adds a separate file named
|
||||||
|
.RI \(dq FILENAME .cmdstatus\(dq
|
||||||
|
for each command output added through the
|
||||||
|
.RI \(dq FILENAME |= CMDLINE \(dq
|
||||||
|
notation (see FILE SPECIFICATIONS).
|
||||||
|
This file contains information about the exit status of the
|
||||||
|
process that executed the command:
|
||||||
|
.
|
||||||
|
.RS 8
|
||||||
|
.TP
|
||||||
|
.RI EXITSTATUS= VALUE
|
||||||
|
Unless
|
||||||
|
.I VALUE
|
||||||
|
is -1, the process ended normally with the specified exit value.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.RI TERMSIG= VALUE
|
||||||
|
Unless
|
||||||
|
.I VALUE
|
||||||
|
is -1, the process was stopped by a signal of the specified number.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.RI WAITPID_ERRNO= VALUE
|
||||||
|
Unless
|
||||||
|
.I VALUE
|
||||||
|
is -1, an attempt to obtain the status of the process failed with the
|
||||||
|
specified error.
|
||||||
|
.PP
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "INPUT OPTIONS"
|
||||||
|
.
|
||||||
|
.OD "files\-from" "F" "FILENAME"
|
||||||
|
Reads input data specifications (see FILE SPECIFICATIONS) from
|
||||||
|
.IR FILENAME ,
|
||||||
|
one specification per line. Each line contains either a file name or a
|
||||||
|
.IR FILENAME := PATH
|
||||||
|
or
|
||||||
|
.IR FILENAME |= CMDLINE
|
||||||
|
specification. Empty lines are ignored.
|
||||||
|
|
||||||
|
A line can also consist of only "\-\-". All lines following this specification
|
||||||
|
are interpreted as simple file names. This is useful for archiving files that
|
||||||
|
contain ":=" or "|=".
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "ignore\-failed\-read" "i" ""
|
||||||
|
Continues after read errors.
|
||||||
|
|
||||||
|
By default,
|
||||||
|
.B dump2tar
|
||||||
|
stops processing after encountering errors while reading an input file.
|
||||||
|
With this option,
|
||||||
|
.B dump2tar
|
||||||
|
prints a warning message and adds an empty entry for the erroneous file in
|
||||||
|
the archive.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "buffer\-size" "b" "VALUE"
|
||||||
|
Reads data from input files in chunks of
|
||||||
|
.I VALUE
|
||||||
|
bytes. Large values can accelerate the archiving process for large files
|
||||||
|
at the cost of increased memory usage. The default value is 1048576.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "file\-timeout" "T" "VALUE"
|
||||||
|
Sets an upper time limit, in seconds, for reading an input file.
|
||||||
|
|
||||||
|
.B dump2tar
|
||||||
|
stops processing a file when the time limit is exceeded. Archive entries for
|
||||||
|
such files are truncated to the amount of data that is collected by the time
|
||||||
|
the limit is reached.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "file\-max\-size" "M" "N"
|
||||||
|
Sets an upper size limit, in bytes, for an input file.
|
||||||
|
|
||||||
|
.B dump2tar
|
||||||
|
stops processing a file when the size limit is exceeded. Archive entries for
|
||||||
|
such files are truncated to the specified size.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "jobs" "j" "N"
|
||||||
|
By default,
|
||||||
|
.B dump2tar
|
||||||
|
processes one file at a time. With this option,
|
||||||
|
.B dump2tar
|
||||||
|
processes
|
||||||
|
.I N
|
||||||
|
files in parallel.
|
||||||
|
|
||||||
|
Parallel processing can accelerate the archiving process,
|
||||||
|
especially if input files are located on slow devices, or when output from
|
||||||
|
multiple commands is added to the archive.
|
||||||
|
|
||||||
|
Note: Use
|
||||||
|
.B tar
|
||||||
|
option \-\-delay\-directory\-restore when extracting files from an archive
|
||||||
|
created with \-\-jobs to prevent conflicts with directory permissions and
|
||||||
|
modification times.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "jobs\-per\-cpu" "J" "N"
|
||||||
|
Processes
|
||||||
|
.I N
|
||||||
|
files for each online CPU in parallel.
|
||||||
|
|
||||||
|
Parallel processing can accelerate the
|
||||||
|
archiving process, especially if input files are located on slow devices, or
|
||||||
|
when output from multiple commands is added to the archive.
|
||||||
|
|
||||||
|
Note: Use
|
||||||
|
.B tar
|
||||||
|
option \-\-delay\-directory\-restore when extracting files from an archive
|
||||||
|
created with \-\-jobs\-per\-cpu to prevent conflicts with directory permissions
|
||||||
|
and modification times.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "exclude" "x" "PATTERN"
|
||||||
|
Does not add files to the archive if their file names match
|
||||||
|
.IR PATTERN .
|
||||||
|
.I PATTERN
|
||||||
|
is an expression that uses the shell wildcards.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "exclude\-from" "X" "FILENAME"
|
||||||
|
Does not add files to the archive if their names match at least one of the
|
||||||
|
patterns listed in the pattern file with name
|
||||||
|
.IR FILENAME .
|
||||||
|
In the pattern file, each line specifies an expression that uses the
|
||||||
|
shell wildcards.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "exclude\-type" "" "TYPE"
|
||||||
|
Does not add files to the archive if they match at least one of the file types
|
||||||
|
specified with
|
||||||
|
.IR TYPE .
|
||||||
|
.I TYPE
|
||||||
|
uses one or more of the characters "fdcbpls", where:
|
||||||
|
|
||||||
|
.RS 8
|
||||||
|
.IP f 3
|
||||||
|
regular files
|
||||||
|
.PP
|
||||||
|
.IP d 3
|
||||||
|
directories
|
||||||
|
.PP
|
||||||
|
.IP c 3
|
||||||
|
character devices
|
||||||
|
.PP
|
||||||
|
.IP b 3
|
||||||
|
block devices
|
||||||
|
.PP
|
||||||
|
.IP p 3
|
||||||
|
named pipes (FIFOs)
|
||||||
|
.PP
|
||||||
|
.IP l 3
|
||||||
|
symbolic links
|
||||||
|
.PP
|
||||||
|
.IP s 3
|
||||||
|
sockets
|
||||||
|
.PP
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "dereference" "" ""
|
||||||
|
Adds the content of link targets instead of symbolic links.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "no\-recursion" "" ""
|
||||||
|
Does not add files from sub\-directories.
|
||||||
|
|
||||||
|
By default,
|
||||||
|
.B dump2tar
|
||||||
|
adds archive entries for specified directories, and for the files within these
|
||||||
|
directories. With this option, a specified directory results in a single entry
|
||||||
|
for the directory. Any contained files to be included must be specified
|
||||||
|
explicitly.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "MISC OPTIONS"
|
||||||
|
.
|
||||||
|
.OD "help" "h" ""
|
||||||
|
Prints an overview of available options, then exits.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "verbose" "V" ""
|
||||||
|
Prints additional informational output.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.OD "quiet" "q" ""
|
||||||
|
Suppresses printing of informational output.
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.
|
||||||
|
.\fB
|
||||||
|
.CL # dump2tar a b \-o archive.tar
|
||||||
|
.\fR
|
||||||
|
|
||||||
|
.RS 4
|
||||||
|
Creates a tar archive named archive.tar containing files a and b.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.\fB
|
||||||
|
.CL # dump2tar /proc \-o procdump.tar.gz \-z \-i \-T 1 \-M 1048576
|
||||||
|
.\fR
|
||||||
|
|
||||||
|
.RS 4
|
||||||
|
Creates a gzip compressed tar archive named procdump.tar.gz that contains
|
||||||
|
all procfs files. Unreadable files are ignored. Files are truncated when the
|
||||||
|
first of the two limiting conditions is reached, either 1048576 bytes of
|
||||||
|
content or the reading time of 1 second.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.\fB
|
||||||
|
.CL # dump2tar '|=dmesg' '|=lspci' \-o data.tar
|
||||||
|
.\fR
|
||||||
|
|
||||||
|
.RS 4
|
||||||
|
Creates a tar archive named data.tar containing the output of the 'dmesg'
|
||||||
|
and 'lspci' commands.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.\fB
|
||||||
|
.CL # dump2tar /sys/kernel/debug/ -x '*/tracing/*' -o debug.tar -i
|
||||||
|
.\fR
|
||||||
|
|
||||||
|
.RS 4
|
||||||
|
Creates a tar archive named debug.tar containing the contents of directory
|
||||||
|
/sys/kernel/debug/ while excluding any file that is located in a sub-directory
|
||||||
|
named 'tracing'.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "EXIT CODES"
|
||||||
|
.TP
|
||||||
|
.B 0
|
||||||
|
The program finished successfully
|
||||||
|
.TP
|
||||||
|
.B 1
|
||||||
|
A run-time error occurred
|
||||||
|
.TP
|
||||||
|
.B 2
|
||||||
|
The specified command was not valid
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.MP dump2tar 1 ,
|
||||||
|
.MP tar 1
|
||||||
31
dump2tar/src/Makefile
Normal file
31
dump2tar/src/Makefile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Common definitions
|
||||||
|
include ../../common.mak
|
||||||
|
|
||||||
|
ALL_CPPFLAGS += -I../include -std=gnu99 -Wno-unused-parameter
|
||||||
|
LDLIBS += -lpthread -lrt
|
||||||
|
ifneq ($(HAVE_ZLIB),0)
|
||||||
|
ALL_CPPFLAGS += -DHAVE_ZLIB
|
||||||
|
LDLIBS += -lz
|
||||||
|
endif
|
||||||
|
|
||||||
|
core_objects = buffer.o dref.o global.o dump.o idcache.o misc.o strarray.o tar.o
|
||||||
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
check_dep_zlib:
|
||||||
|
$(call check_dep, \
|
||||||
|
"dump2tar", \
|
||||||
|
"zlib.h", \
|
||||||
|
"zlib-devel or libz-dev", \
|
||||||
|
"HAVE_ZLIB=0")
|
||||||
|
|
||||||
|
all: check_dep_zlib dump2tar
|
||||||
|
|
||||||
|
dump2tar: $(core_objects) dump2tar.o $(libs)
|
||||||
|
|
||||||
|
install: dump2tar
|
||||||
|
$(INSTALL) -c dump2tar $(DESTDIR)$(USRBINDIR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f dump2tar *.o
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
273
dump2tar/src/buffer.c
Normal file
273
dump2tar/src/buffer.c
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Data buffering functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void buffer_print(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "DEBUG: buffer at %p\n", (void *) buffer);
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
fprintf(stderr, "DEBUG: total=%zu\n", buffer->total);
|
||||||
|
fprintf(stderr, "DEBUG: off=%zu\n", buffer->off);
|
||||||
|
fprintf(stderr, "DEBUG: size=%zu\n", buffer->size);
|
||||||
|
fprintf(stderr, "DEBUG: addr=%p\n", (void *) buffer->addr);
|
||||||
|
fprintf(stderr, "DEBUG: fd_open=%d\n", buffer->fd_open);
|
||||||
|
fprintf(stderr, "DEBUG: fd=%d\n", buffer->fd);
|
||||||
|
if (buffer->fd_open) {
|
||||||
|
fprintf(stderr, "DEBUG: fd->pos=%zu\n",
|
||||||
|
lseek(buffer->fd, 0, SEEK_CUR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize @buffer to hold @size bytes in memory */
|
||||||
|
void buffer_init(struct buffer *buffer, size_t size)
|
||||||
|
{
|
||||||
|
memset(buffer, 0, sizeof(struct buffer));
|
||||||
|
buffer->addr = mmalloc(size);
|
||||||
|
buffer->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a new buffer for holding @size bytes in memory */
|
||||||
|
struct buffer *buffer_alloc(size_t size)
|
||||||
|
{
|
||||||
|
struct buffer *buffer;
|
||||||
|
|
||||||
|
buffer = mmalloc(sizeof(struct buffer));
|
||||||
|
buffer_init(buffer, size);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forget about any data stored in @buffer */
|
||||||
|
void buffer_reset(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
buffer->total = 0;
|
||||||
|
buffer->off = 0;
|
||||||
|
if (buffer->fd_open) {
|
||||||
|
if (ftruncate(buffer->fd, 0))
|
||||||
|
mwarn("Cannot truncate temporary file");
|
||||||
|
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1)
|
||||||
|
mwarn("Cannot seek in temporary file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close buffer file associated with @buffer */
|
||||||
|
void buffer_close(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
if (!buffer->fd_open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fclose(buffer->file);
|
||||||
|
buffer->fd = 0;
|
||||||
|
buffer->fd_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release all resources associated with @buffer. If @dyn is %true, also free
|
||||||
|
* @buffer itself. */
|
||||||
|
void buffer_free(struct buffer *buffer, bool dyn)
|
||||||
|
{
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
buffer_reset(buffer);
|
||||||
|
buffer_close(buffer);
|
||||||
|
free(buffer->addr);
|
||||||
|
if (dyn)
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open a buffer file for @buffer. Return %EXIT_OK on success, %EXIT_RUNTIME
|
||||||
|
* otherwise. */
|
||||||
|
int buffer_open(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer->fd_open)
|
||||||
|
return EXIT_OK;
|
||||||
|
|
||||||
|
buffer->file = tmpfile();
|
||||||
|
if (!buffer->file) {
|
||||||
|
mwarn("Could not create temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->fd = fileno(buffer->file);
|
||||||
|
buffer->fd_open = true;
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write data in memory of @buffer to buffer file. Return %EXIT_OK on success,
|
||||||
|
* %EXIT_RUNTIME otherwise. */
|
||||||
|
int buffer_flush(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer->off == 0)
|
||||||
|
return EXIT_OK;
|
||||||
|
if (buffer_open(buffer))
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
if (misc_write_data(buffer->fd, buffer->addr, buffer->off)) {
|
||||||
|
mwarn("Could not write to temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
buffer->off = 0;
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to ensure that at least @size bytes are available at
|
||||||
|
* @buffer->addr[buffer->off]. Return the actual number of bytes available or
|
||||||
|
* @-1 on error. If @usefile is %true, make use of a buffer file if
|
||||||
|
* the total buffer size exceeds @max_buffer_size. */
|
||||||
|
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
|
||||||
|
size_t max_buffer_size)
|
||||||
|
{
|
||||||
|
size_t needsize;
|
||||||
|
|
||||||
|
if (size > max_buffer_size && usefile)
|
||||||
|
size = max_buffer_size;
|
||||||
|
|
||||||
|
needsize = buffer->off + size;
|
||||||
|
if (needsize <= buffer->size) {
|
||||||
|
/* Room available */
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsize > max_buffer_size && usefile) {
|
||||||
|
/* Need to write out memory buffer to buffer file */
|
||||||
|
if (buffer_flush(buffer))
|
||||||
|
return -1;
|
||||||
|
if (size <= buffer->size)
|
||||||
|
return size;
|
||||||
|
needsize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to increase memory buffer size */
|
||||||
|
buffer->size = needsize;
|
||||||
|
buffer->addr = mrealloc(buffer->addr, buffer->size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to read @chunk bytes from @fd to @buffer. Return the number of bytes
|
||||||
|
* read on success, %0 on EOF or %-1 on error. */
|
||||||
|
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
|
||||||
|
bool usefile, size_t max_buffer_size)
|
||||||
|
{
|
||||||
|
ssize_t c = buffer_make_room(buffer, chunk, usefile, max_buffer_size);
|
||||||
|
|
||||||
|
DBG("buffer_read_fd wanted %zd got %zd", chunk, c);
|
||||||
|
if (c < 0)
|
||||||
|
return c;
|
||||||
|
|
||||||
|
c = read(fd, buffer->addr + buffer->off, c);
|
||||||
|
if (c > 0) {
|
||||||
|
buffer->total += c;
|
||||||
|
buffer->off += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add @len bytes at @addr to @buffer. If @addr is %NULL, add zeroes. Return
|
||||||
|
* %EXIT_OK on success, %EXIT_RUNTIME otherwise. */
|
||||||
|
int buffer_add_data(struct buffer *buffer, char *addr, size_t len, bool usefile,
|
||||||
|
size_t max_buffer_size)
|
||||||
|
{
|
||||||
|
ssize_t c;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
c = buffer_make_room(buffer, len, usefile, max_buffer_size);
|
||||||
|
if (c < 0)
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
if (addr) {
|
||||||
|
memcpy(buffer->addr + buffer->off, addr, c);
|
||||||
|
addr += c;
|
||||||
|
} else {
|
||||||
|
memset(buffer->addr + buffer->off, 0, c);
|
||||||
|
}
|
||||||
|
buffer->total += c;
|
||||||
|
buffer->off += c;
|
||||||
|
|
||||||
|
len -= c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call @cb for all chunks of data in @buffer. @data is passed to @cb. */
|
||||||
|
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
if (buffer->total == 0)
|
||||||
|
return EXIT_OK;
|
||||||
|
|
||||||
|
if (!buffer->fd_open)
|
||||||
|
return cb(data, buffer->addr, buffer->off);
|
||||||
|
|
||||||
|
/* Free memory buffer to be used as copy buffer */
|
||||||
|
if (buffer_flush(buffer))
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1) {
|
||||||
|
mwarn("Cannot seek in temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data from temporary file to target file */
|
||||||
|
while ((r = misc_read_data(buffer->fd, buffer->addr,
|
||||||
|
buffer->size)) != 0) {
|
||||||
|
if (r < 0) {
|
||||||
|
mwarn("Cannot read from temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
rc = cb(data, buffer->addr, r);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Truncate @buffer to at most @len bytes */
|
||||||
|
int buffer_truncate(struct buffer *buffer, size_t len)
|
||||||
|
{
|
||||||
|
size_t delta;
|
||||||
|
|
||||||
|
if (buffer->total <= len)
|
||||||
|
return EXIT_OK;
|
||||||
|
|
||||||
|
delta = buffer->total - len;
|
||||||
|
|
||||||
|
buffer->total = len;
|
||||||
|
if (buffer->fd_open && delta > buffer->off) {
|
||||||
|
/* All of memory and some of file buffer is truncated */
|
||||||
|
buffer->off = 0;
|
||||||
|
if (ftruncate(buffer->fd, len)) {
|
||||||
|
mwarn("Cannot truncate temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
if (lseek(buffer->fd, len, SEEK_SET) == (off_t) -1) {
|
||||||
|
mwarn("Cannot seek in temporary file");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Only memory buffer is truncated */
|
||||||
|
buffer->off -= delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
94
dump2tar/src/dref.c
Normal file
94
dump2tar/src/dref.c
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Reference counting for directory handles
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "dref.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/* dref_mutex serializes access to drefs */
|
||||||
|
static pthread_mutex_t dref_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static unsigned long num_open_dirs;
|
||||||
|
static unsigned long num_open_dirs_max;
|
||||||
|
|
||||||
|
/* Lock dref mutex */
|
||||||
|
static void dref_lock(void)
|
||||||
|
{
|
||||||
|
if (!global_threaded)
|
||||||
|
return;
|
||||||
|
pthread_mutex_lock(&dref_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock dref mutex */
|
||||||
|
static void dref_unlock(void)
|
||||||
|
{
|
||||||
|
if (!global_threaded)
|
||||||
|
return;
|
||||||
|
pthread_mutex_unlock(&dref_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a reference count managed directory handle for @dirname */
|
||||||
|
struct dref *dref_create(const char *dirname)
|
||||||
|
{
|
||||||
|
struct dref *dref;
|
||||||
|
DIR *dd;
|
||||||
|
|
||||||
|
dd = opendir(dirname);
|
||||||
|
DBG("opendir(%s)=%p (total=%lu)", dirname, dd, ++num_open_dirs);
|
||||||
|
if (!dd) {
|
||||||
|
num_open_dirs--;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_open_dirs > num_open_dirs_max)
|
||||||
|
num_open_dirs_max = num_open_dirs;
|
||||||
|
|
||||||
|
dref = mmalloc(sizeof(struct dref));
|
||||||
|
dref->dd = dd;
|
||||||
|
dref->dirfd = dirfd(dd);
|
||||||
|
dref->count = 1;
|
||||||
|
|
||||||
|
return dref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain a reference to @dref */
|
||||||
|
struct dref *dref_get(struct dref *dref)
|
||||||
|
{
|
||||||
|
if (dref) {
|
||||||
|
dref_lock();
|
||||||
|
dref->count++;
|
||||||
|
dref_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release a reference to @dref. If this was the last reference, lose the
|
||||||
|
* associated directory handle and free @dref. */
|
||||||
|
void dref_put(struct dref *dref)
|
||||||
|
{
|
||||||
|
if (dref) {
|
||||||
|
dref_lock();
|
||||||
|
dref->count--;
|
||||||
|
if (dref->count == 0) {
|
||||||
|
num_open_dirs--;
|
||||||
|
DBG("closedir(%p) (total=%lu, max=%lu)", dref->dd,
|
||||||
|
num_open_dirs, num_open_dirs_max);
|
||||||
|
closedir(dref->dd);
|
||||||
|
free(dref);
|
||||||
|
}
|
||||||
|
dref_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
1859
dump2tar/src/dump.c
Normal file
1859
dump2tar/src/dump.c
Normal file
File diff suppressed because it is too large
Load Diff
478
dump2tar/src/dump2tar.c
Normal file
478
dump2tar/src/dump2tar.c
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Command line interface
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
|
||||||
|
#include "dump.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "idcache.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "strarray.h"
|
||||||
|
|
||||||
|
#define MIN_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
#define OPT_NOSHORT_BASE 256
|
||||||
|
|
||||||
|
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
||||||
|
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
||||||
|
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
||||||
|
|
||||||
|
/* Program description */
|
||||||
|
static const struct util_prg dump2tar_prg = {
|
||||||
|
.desc = "Use dump2tar to create a tar archive from the contents "
|
||||||
|
"of arbitrary files.\nIt works even when the size of actual "
|
||||||
|
"file content is not known beforehand,\nsuch as with FIFOs, "
|
||||||
|
"character devices or certain Linux debugfs or sysfs files.\n"
|
||||||
|
"\nYou can also add files under different names and add "
|
||||||
|
"command output using the\nformat described in section SPECS "
|
||||||
|
"below. When no additional options are\nspecified, the "
|
||||||
|
"resulting archive is written to the standard output stream\n"
|
||||||
|
"in uncompressed tar format.",
|
||||||
|
.args = "SPECS",
|
||||||
|
.copyright_vec = {
|
||||||
|
{ "IBM Corp.", 2016, 2016 },
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Definition of command line options */
|
||||||
|
static struct util_opt dump2tar_opts[] = {
|
||||||
|
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "output-file", required_argument, NULL, 'o' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Write archive to FILE (default: standard output)",
|
||||||
|
},
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
{
|
||||||
|
.option = { "gzip", no_argument, NULL, 'z' },
|
||||||
|
.desc = "Write a gzip compressed archive",
|
||||||
|
},
|
||||||
|
#endif /* HAVE_ZLIB */
|
||||||
|
{
|
||||||
|
.option = { "max-size", required_argument, NULL, 'm' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Stop adding files when archive size exceeds N bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "timeout", required_argument, NULL, 't' },
|
||||||
|
.argument = "SEC",
|
||||||
|
.desc = "Stop adding files after SEC seconds",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "no-eof", no_argument, NULL, 131 },
|
||||||
|
.desc = "Do not write an end-of-file marker",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
||||||
|
.desc = "Add status of commands as separate file",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "append", no_argument, NULL, 133 },
|
||||||
|
.desc = "Append output to end of file",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
|
||||||
|
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "files-from", required_argument, NULL, 'F' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Read filenames from FILE (- for standard input)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
||||||
|
.desc = "Continue after read errors",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "buffer-size", required_argument, NULL, 'b' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read data in chunks of N byte (default: 16384)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||||
|
.desc = "Stop reading file after SEC seconds",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Stop reading file after N bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "jobs", required_argument, NULL, 'j' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read N files in parallel (default: 1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read N files per CPU in parallel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude", required_argument, NULL, 'x' },
|
||||||
|
.argument = "PATTERN",
|
||||||
|
.desc = "Don't add files matching PATTERN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude-from", required_argument, NULL, 'X' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Don't add files matching patterns in FILE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude-type", required_argument, NULL,
|
||||||
|
OPT_EXCLUDETYPE },
|
||||||
|
.argument = "TYPE",
|
||||||
|
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
||||||
|
.desc = "Add link targets instead of links",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "no-recursion", no_argument, NULL,
|
||||||
|
OPT_NORECURSION },
|
||||||
|
.desc = "Don't add files from sub-directories",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
|
||||||
|
UTIL_OPT_SECTION("MISC OPTIONS"),
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Print additional informational output",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "quiet", no_argument, NULL, 'q' },
|
||||||
|
.desc = "Suppress printing of informational output",
|
||||||
|
},
|
||||||
|
UTIL_OPT_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Split buffer size specification in @arg into two numbers to be stored in
|
||||||
|
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
|
||||||
|
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
unsigned long from, to;
|
||||||
|
|
||||||
|
if (!*arg) {
|
||||||
|
mwarnx("Empty buffer size specified");
|
||||||
|
return EXIT_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
from = strtoul(arg, &err, 10);
|
||||||
|
|
||||||
|
if (*err == '-')
|
||||||
|
to = strtoul(err + 1, &err, 10);
|
||||||
|
else
|
||||||
|
to = *to_ptr;
|
||||||
|
|
||||||
|
if (*err) {
|
||||||
|
mwarnx("Invalid buffer size: %s", arg);
|
||||||
|
return EXIT_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from < MIN_BUFFER_SIZE || to < MIN_BUFFER_SIZE) {
|
||||||
|
mwarnx("Buffer size too low (minimum %u)", MIN_BUFFER_SIZE);
|
||||||
|
return EXIT_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to < from)
|
||||||
|
to = from;
|
||||||
|
|
||||||
|
*from_ptr = from;
|
||||||
|
*to_ptr = to;
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_and_add_spec(struct dump_opts *opts, const char *spec)
|
||||||
|
{
|
||||||
|
char *op, *s, *inname, *outname = NULL;
|
||||||
|
bool is_cmd = false;
|
||||||
|
|
||||||
|
s = mstrdup(spec);
|
||||||
|
op = strstr(s, "|=");
|
||||||
|
if (op)
|
||||||
|
is_cmd = true;
|
||||||
|
else
|
||||||
|
op = strstr(s, ":=");
|
||||||
|
|
||||||
|
if (op) {
|
||||||
|
*op = 0;
|
||||||
|
inname = op + 2;
|
||||||
|
outname = s;
|
||||||
|
} else {
|
||||||
|
inname = s;
|
||||||
|
}
|
||||||
|
dump_opts_add_spec(opts, inname, outname, is_cmd);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_specs_from_file(struct dump_opts *opts, const char *filename)
|
||||||
|
{
|
||||||
|
FILE *fd;
|
||||||
|
char *line = NULL;
|
||||||
|
size_t line_size;
|
||||||
|
int rc = EXIT_RUNTIME;
|
||||||
|
bool need_close = false, parse_spec = true;
|
||||||
|
|
||||||
|
if (strcmp(filename, "-") == 0)
|
||||||
|
fd = stdin;
|
||||||
|
else {
|
||||||
|
fd = fopen(filename, "r");
|
||||||
|
if (!fd) {
|
||||||
|
mwarn("%s: Cannot open file", filename);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
need_close = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((getline(&line, &line_size, fd) != -1)) {
|
||||||
|
chomp(line, "\n");
|
||||||
|
if (line[0] == 0)
|
||||||
|
continue;
|
||||||
|
if (parse_spec && strcmp(line, "--") == 0) {
|
||||||
|
/* After a line containing --, no more := or |= specs
|
||||||
|
* are expected */
|
||||||
|
parse_spec = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (parse_spec)
|
||||||
|
parse_and_add_spec(opts, line);
|
||||||
|
else
|
||||||
|
dump_opts_add_spec(opts, line, NULL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ferror(fd))
|
||||||
|
mwarn("%s: Cannot read file", filename);
|
||||||
|
else
|
||||||
|
rc = EXIT_OK;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (need_close)
|
||||||
|
fclose(fd);
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help(void)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
const char *desc;
|
||||||
|
} specs[] = {
|
||||||
|
{ "PATH", "Add file or directory at PATH" },
|
||||||
|
{ "NEWPATH:=PATH", "Add file or directory at PATH as NEWPATH" },
|
||||||
|
{ "NEWPATH|=CMDLINE", "Add output of command line CMDLINE as "
|
||||||
|
"NEWPATH" },
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
util_prg_print_help();
|
||||||
|
printf("SPECS\n");
|
||||||
|
for (i = 0; specs[i].name; i++)
|
||||||
|
util_opt_print_indented(specs[i].name, specs[i].desc);
|
||||||
|
printf("\n");
|
||||||
|
util_opt_print_help();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int rc = EXIT_USAGE, opt;
|
||||||
|
long i;
|
||||||
|
struct dump_opts *opts;
|
||||||
|
|
||||||
|
if (getenv("DUMP2TAR_DEBUG"))
|
||||||
|
global_debug = true;
|
||||||
|
|
||||||
|
util_prg_init(&dump2tar_prg);
|
||||||
|
util_opt_init(dump2tar_opts, "-");
|
||||||
|
misc_init();
|
||||||
|
|
||||||
|
opts = dump_opts_new();
|
||||||
|
opterr = 0;
|
||||||
|
while ((opt = util_opt_getopt_long(argc, argv)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h': /* --help */
|
||||||
|
print_help();
|
||||||
|
rc = EXIT_OK;
|
||||||
|
goto out;
|
||||||
|
case 'v': /* --version */
|
||||||
|
util_prg_print_version();
|
||||||
|
rc = EXIT_OK;
|
||||||
|
goto out;
|
||||||
|
case 'V': /* --verbose */
|
||||||
|
global_verbose = true;
|
||||||
|
global_quiet = false;
|
||||||
|
opts->verbose = true;
|
||||||
|
opts->quiet = false;
|
||||||
|
break;
|
||||||
|
case 'q': /* --quiet */
|
||||||
|
global_quiet = true;
|
||||||
|
global_verbose = false;
|
||||||
|
opts->quiet = true;
|
||||||
|
opts->verbose = false;
|
||||||
|
break;
|
||||||
|
case 'i': /* --ignore-failed-read */
|
||||||
|
opts->ignore_failed_read = true;
|
||||||
|
break;
|
||||||
|
case 'j': /* --jobs N */
|
||||||
|
opts->jobs = atoi(optarg);
|
||||||
|
if (opts->jobs < 1) {
|
||||||
|
mwarnx("Invalid number of jobs: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'J': /* --jobs-per-cpu N */
|
||||||
|
opts->jobs_per_cpu = atoi(optarg);
|
||||||
|
if (opts->jobs_per_cpu < 1) {
|
||||||
|
mwarnx("Invalid number of jobs: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'b': /* --buffer-size N */
|
||||||
|
if (parse_buffer_size(optarg, &opts->read_chunk_size,
|
||||||
|
&opts->max_buffer_size))
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
case 'x': /* --exclude PATTERN */
|
||||||
|
add_str_to_strarray(&opts->exclude, optarg);
|
||||||
|
break;
|
||||||
|
case 'X': /* --exclude-from FILE */
|
||||||
|
if (add_file_to_strarray(&opts->exclude, optarg))
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
case 'F': /* --files-from FILE */
|
||||||
|
if (add_specs_from_file(opts, optarg))
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
case 'o': /* --output-file FILE */
|
||||||
|
if (opts->output_file) {
|
||||||
|
mwarnx("Output file specified multiple times");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
opts->output_file = optarg;
|
||||||
|
break;
|
||||||
|
case OPT_DEREFERENCE: /* --dereference */
|
||||||
|
opts->dereference = true;
|
||||||
|
break;
|
||||||
|
case OPT_NORECURSION: /* --no-recursion */
|
||||||
|
opts->recursive = false;
|
||||||
|
break;
|
||||||
|
case OPT_EXCLUDETYPE: /* --exclude-type TYPE */
|
||||||
|
for (i = 0; optarg[i]; i++) {
|
||||||
|
if (dump_opts_set_type_excluded(opts,
|
||||||
|
optarg[i]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (optarg[i]) {
|
||||||
|
mwarnx("Unrecognized file type: %c", optarg[i]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 131: /* --no-eof */
|
||||||
|
opts->no_eof = true;
|
||||||
|
break;
|
||||||
|
case 132: /* --add-cmd-status */
|
||||||
|
opts->add_cmd_status = true;
|
||||||
|
break;
|
||||||
|
case 133: /* --append */
|
||||||
|
opts->append = true;
|
||||||
|
break;
|
||||||
|
case 't': /* --timeout VALUE */
|
||||||
|
opts->timeout = atoi(optarg);
|
||||||
|
if (opts->timeout < 1) {
|
||||||
|
mwarnx("Invalid timeout value: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'T': /* --file-timeout VALUE */
|
||||||
|
opts->file_timeout = atoi(optarg);
|
||||||
|
if (opts->file_timeout < 1) {
|
||||||
|
mwarnx("Invalid timeout value: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm': /* --max-size N */
|
||||||
|
opts->max_size = atol(optarg);
|
||||||
|
if (opts->max_size < 2) {
|
||||||
|
mwarnx("Invalid maximum size: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'M': /* --file-max-size N */
|
||||||
|
opts->file_max_size = atol(optarg);
|
||||||
|
if (opts->file_max_size < 2) {
|
||||||
|
mwarnx("Invalid maximum size: %s", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'z': /* --gzip */
|
||||||
|
opts->gzip = true;
|
||||||
|
break;
|
||||||
|
case 1: /* Filename specification or unrecognized option */
|
||||||
|
if (optarg[0] == '-') {
|
||||||
|
mwarnx("Invalid option '%s'", optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
parse_and_add_spec(opts, optarg);
|
||||||
|
break;
|
||||||
|
case '?': /* Unrecognized option */
|
||||||
|
if (optopt)
|
||||||
|
mwarnx("Invalid option '-%c'", optopt);
|
||||||
|
else
|
||||||
|
mwarnx("Invalid option '%s'", argv[optind - 1]);
|
||||||
|
goto out;
|
||||||
|
case ':': /* Missing argument */
|
||||||
|
mwarnx("Option '%s' requires an argument",
|
||||||
|
argv[optind - 1]);
|
||||||
|
goto out;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optind >= argc && opts->num_specs == 0) {
|
||||||
|
mwarnx("Please specify files to dump");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = optind; i < argc; i++)
|
||||||
|
dump_opts_add_spec(opts, argv[i], NULL, false);
|
||||||
|
|
||||||
|
rc = dump_to_tar(opts);
|
||||||
|
|
||||||
|
out:
|
||||||
|
idcache_cleanup();
|
||||||
|
misc_cleanup();
|
||||||
|
dump_opts_free(opts);
|
||||||
|
|
||||||
|
if (rc == EXIT_USAGE)
|
||||||
|
util_prg_print_parse_error();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
20
dump2tar/src/global.c
Normal file
20
dump2tar/src/global.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Global variables
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
/* Global settings */
|
||||||
|
bool global_threaded;
|
||||||
|
bool global_debug;
|
||||||
|
bool global_verbose;
|
||||||
|
bool global_quiet;
|
||||||
|
bool global_timestamps;
|
||||||
155
dump2tar/src/idcache.c
Normal file
155
dump2tar/src/idcache.c
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Caches for user and group ID lookups
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "idcache.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/* Maximum user and group name lengths as defined in tar header */
|
||||||
|
#define ID_NAME_MAXLEN 32
|
||||||
|
|
||||||
|
/* Types for user and group ID caches */
|
||||||
|
typedef uid_t generic_id_t; /* Assumes that uid_t == gid_t */
|
||||||
|
|
||||||
|
struct id_cache_entry {
|
||||||
|
generic_id_t id;
|
||||||
|
char name[ID_NAME_MAXLEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct id_cache {
|
||||||
|
unsigned int num;
|
||||||
|
struct id_cache_entry entries[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* cache_mutex serializes access to cached uid and gid data */
|
||||||
|
static pthread_mutex_t id_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static struct id_cache *id_cache_uid;
|
||||||
|
static struct id_cache *id_cache_gid;
|
||||||
|
|
||||||
|
/* Lock cache mutex */
|
||||||
|
static void cache_lock(void)
|
||||||
|
{
|
||||||
|
if (!global_threaded)
|
||||||
|
return;
|
||||||
|
pthread_mutex_lock(&id_cache_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock cache mutex */
|
||||||
|
static void cache_unlock(void)
|
||||||
|
{
|
||||||
|
if (!global_threaded)
|
||||||
|
return;
|
||||||
|
pthread_mutex_unlock(&id_cache_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the name associated with @id in @cache to at most @len bytes at @dest.
|
||||||
|
* Return %true if name was found in cache, %false otherwise. */
|
||||||
|
static bool strncpy_id_cache_entry(char *dest, struct id_cache *cache,
|
||||||
|
generic_id_t id, size_t len)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
bool hit = false;
|
||||||
|
|
||||||
|
cache_lock();
|
||||||
|
if (cache) {
|
||||||
|
for (i = 0; i < cache->num; i++) {
|
||||||
|
if (cache->entries[i].id == id) {
|
||||||
|
strncpy(dest, cache->entries[i].name, len);
|
||||||
|
hit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache_unlock();
|
||||||
|
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a new entry consisting of @id and @name to ID cache in @*cache_ptr.
|
||||||
|
* Update @cache_ptr if necessary. */
|
||||||
|
static void add_id_cache_entry(struct id_cache **cache_ptr, generic_id_t id,
|
||||||
|
char *name)
|
||||||
|
{
|
||||||
|
struct id_cache *cache;
|
||||||
|
unsigned int cache_num;
|
||||||
|
size_t new_size;
|
||||||
|
struct id_cache *new_cache;
|
||||||
|
|
||||||
|
cache_lock();
|
||||||
|
|
||||||
|
cache = *cache_ptr;
|
||||||
|
cache_num = cache ? cache->num : 0;
|
||||||
|
new_size = sizeof(struct id_cache) +
|
||||||
|
sizeof(struct id_cache_entry) * (cache_num + 1);
|
||||||
|
new_cache = mrealloc(cache, new_size);
|
||||||
|
if (cache_num == 0)
|
||||||
|
new_cache->num = 0;
|
||||||
|
new_cache->entries[cache_num].id = id;
|
||||||
|
strncpy(new_cache->entries[cache_num].name, name, ID_NAME_MAXLEN);
|
||||||
|
new_cache->num++;
|
||||||
|
*cache_ptr = new_cache;
|
||||||
|
|
||||||
|
cache_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the user name corresponding to user ID @uid to at most @len bytes
|
||||||
|
* at @name */
|
||||||
|
void uid_to_name(uid_t uid, char *name, size_t len)
|
||||||
|
{
|
||||||
|
struct passwd pwd, *pwd_ptr;
|
||||||
|
char buffer[PWD_BUFFER_SIZE], *result;
|
||||||
|
|
||||||
|
if (strncpy_id_cache_entry(name, id_cache_uid, uid, len))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* getpwuid() can be slow so cache results */
|
||||||
|
getpwuid_r(uid, &pwd, buffer, PWD_BUFFER_SIZE, &pwd_ptr);
|
||||||
|
if (!pwd_ptr || !pwd_ptr->pw_name)
|
||||||
|
return;
|
||||||
|
result = pwd_ptr->pw_name;
|
||||||
|
|
||||||
|
add_id_cache_entry(&id_cache_uid, uid, result);
|
||||||
|
|
||||||
|
strncpy(name, result, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the group name corresponding to group ID @gid to at most @len bytes
|
||||||
|
* at @name */
|
||||||
|
void gid_to_name(gid_t gid, char *name, size_t len)
|
||||||
|
{
|
||||||
|
struct group grp, *grp_ptr;
|
||||||
|
char buffer[GRP_BUFFER_SIZE], *result;
|
||||||
|
|
||||||
|
if (strncpy_id_cache_entry(name, id_cache_gid, gid, len))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* getgrgid() can be slow so cache results */
|
||||||
|
getgrgid_r(gid, &grp, buffer, GRP_BUFFER_SIZE, &grp_ptr);
|
||||||
|
if (!grp_ptr || !grp_ptr->gr_name)
|
||||||
|
return;
|
||||||
|
result = grp_ptr->gr_name;
|
||||||
|
|
||||||
|
add_id_cache_entry(&id_cache_gid, gid, result);
|
||||||
|
|
||||||
|
strncpy(name, result, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void idcache_cleanup(void)
|
||||||
|
{
|
||||||
|
free(id_cache_uid);
|
||||||
|
free(id_cache_gid);
|
||||||
|
}
|
||||||
492
dump2tar/src/misc.c
Normal file
492
dump2tar/src/misc.c
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Helper functions
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "dref.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
struct timespec main_start_ts;
|
||||||
|
|
||||||
|
static pthread_key_t thread_name_key;
|
||||||
|
static bool stdout_data;
|
||||||
|
|
||||||
|
/* Write @len bytes at @addr to @fd. Return %EXIT_OK on success, %EXIT_RUNTIME
|
||||||
|
* otherwise. */
|
||||||
|
int misc_write_data(int fd, char *addr, size_t len)
|
||||||
|
{
|
||||||
|
ssize_t w;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
w = write(fd, addr, len);
|
||||||
|
if (w < 0)
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
len -= w;
|
||||||
|
addr += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read at most @len bytes from @fd to @addr. Return the number of bytes read
|
||||||
|
* or %-1 on error. */
|
||||||
|
ssize_t misc_read_data(int fd, char *addr, size_t len)
|
||||||
|
{
|
||||||
|
size_t done = 0;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
r = read(fd, addr, len);
|
||||||
|
if (r < 0)
|
||||||
|
return -1;
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
len -= r;
|
||||||
|
addr += r;
|
||||||
|
done += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance timespec @ts by @sec seconds and @nsec nanoseconds */
|
||||||
|
void inc_timespec(struct timespec *ts, time_t sec, long nsec)
|
||||||
|
{
|
||||||
|
ts->tv_nsec += nsec;
|
||||||
|
ts->tv_sec += sec;
|
||||||
|
if (ts->tv_nsec > NSEC_PER_SEC) {
|
||||||
|
ts->tv_nsec -= NSEC_PER_SEC;
|
||||||
|
ts->tv_sec++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set timespec @ts to point to @sec seconds and @nsec nanoseconds in the
|
||||||
|
* future */
|
||||||
|
void set_timespec(struct timespec *ts, time_t sec, long nsec)
|
||||||
|
{
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, ts);
|
||||||
|
inc_timespec(ts, sec, nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if timespec @a refers to a point in time before @b */
|
||||||
|
bool ts_before(struct timespec *a, struct timespec *b)
|
||||||
|
{
|
||||||
|
if (a->tv_sec < b->tv_sec ||
|
||||||
|
(a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store a string representing the time duration between @start and @end in
|
||||||
|
* at most @len bytes of @buff. */
|
||||||
|
int snprintf_duration(char *buff, size_t len, struct timespec *start,
|
||||||
|
struct timespec *end)
|
||||||
|
{
|
||||||
|
time_t sec;
|
||||||
|
long nsec, msec, s, m, h;
|
||||||
|
|
||||||
|
sec = end->tv_sec - start->tv_sec;
|
||||||
|
nsec = end->tv_nsec - start->tv_nsec;
|
||||||
|
|
||||||
|
if (nsec < 0) {
|
||||||
|
nsec += NSEC_PER_SEC;
|
||||||
|
sec--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msec = nsec / NSEC_PER_MSEC;
|
||||||
|
s = sec % 60;
|
||||||
|
sec /= 60;
|
||||||
|
m = sec % 60;
|
||||||
|
sec /= 60;
|
||||||
|
h = sec;
|
||||||
|
|
||||||
|
if (h > 0)
|
||||||
|
return snprintf(buff, len, "%luh%lum%lu.%03lus", h, m, s, msec);
|
||||||
|
else if (m > 0)
|
||||||
|
return snprintf(buff, len, "%lum%lu.%03lus", m, s, msec);
|
||||||
|
else
|
||||||
|
return snprintf(buff, len, "%lu.%03lus", s, msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the name of the current thread */
|
||||||
|
char *get_threadname(void)
|
||||||
|
{
|
||||||
|
return pthread_getspecific(thread_name_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snprintf_timestamp(char *str, size_t size)
|
||||||
|
{
|
||||||
|
struct timespec now_ts;
|
||||||
|
|
||||||
|
set_timespec(&now_ts, 0, 0);
|
||||||
|
now_ts.tv_sec -= main_start_ts.tv_sec;
|
||||||
|
now_ts.tv_nsec -= main_start_ts.tv_nsec;
|
||||||
|
if (now_ts.tv_nsec < 0) {
|
||||||
|
now_ts.tv_nsec += NSEC_PER_SEC;
|
||||||
|
now_ts.tv_sec--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snprintf(str, size, "[%3lu.%06lu] ", now_ts.tv_sec,
|
||||||
|
now_ts.tv_nsec / NSEC_PER_USEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When DUMP2TAR_DEBUG is set to non-zero, print debugging information */
|
||||||
|
void debug(const char *file, unsigned long line, const char *format, ...)
|
||||||
|
{
|
||||||
|
char msg[MSG_LEN];
|
||||||
|
size_t off = 0;
|
||||||
|
int rc;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
/* Debug marker */
|
||||||
|
rc = snprintf(&msg[off], MSG_LEN - off, "DEBUG: ");
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
/* Timestamp */
|
||||||
|
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
/* Thread name */
|
||||||
|
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", get_threadname());
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
/* Message */
|
||||||
|
va_start(args, format);
|
||||||
|
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
|
||||||
|
va_end(args);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
/* Call site */
|
||||||
|
rc = snprintf(&msg[off], MSG_LEN - off, " (%s:%lu)", file, line);
|
||||||
|
|
||||||
|
out:
|
||||||
|
fprintf(stderr, "%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print a warning message consisting of @format and variable arguments.
|
||||||
|
* If @print_errno is true, also print the text corresponding to errno.
|
||||||
|
* We're not using err.h's warn since we want timestamps and synchronized
|
||||||
|
* output. */
|
||||||
|
void _mwarn(bool print_errno, const char *format, ...)
|
||||||
|
{
|
||||||
|
char msg[MSG_LEN];
|
||||||
|
size_t off = 0;
|
||||||
|
int rc;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (global_timestamps) {
|
||||||
|
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ",
|
||||||
|
program_invocation_short_name);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
|
||||||
|
va_end(args);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
|
||||||
|
if (print_errno)
|
||||||
|
snprintf(&msg[off], MSG_LEN - off, ": %s", strerror(errno));
|
||||||
|
|
||||||
|
out:
|
||||||
|
fprintf(stderr, "%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Provide informational output if --verbose was specified */
|
||||||
|
void verb(const char *format, ...)
|
||||||
|
{
|
||||||
|
char msg[MSG_LEN];
|
||||||
|
size_t off = 0;
|
||||||
|
int rc;
|
||||||
|
va_list args;
|
||||||
|
FILE *fd;
|
||||||
|
|
||||||
|
if (!global_verbose)
|
||||||
|
return;
|
||||||
|
if (stdout_data)
|
||||||
|
fd = stderr;
|
||||||
|
else
|
||||||
|
fd = stdout;
|
||||||
|
if (global_timestamps) {
|
||||||
|
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
out:
|
||||||
|
fprintf(fd, "%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Provide informational output. */
|
||||||
|
void info(const char *format, ...)
|
||||||
|
{
|
||||||
|
char msg[MSG_LEN];
|
||||||
|
size_t off = 0;
|
||||||
|
int rc;
|
||||||
|
va_list args;
|
||||||
|
FILE *fd;
|
||||||
|
|
||||||
|
if (global_quiet)
|
||||||
|
return;
|
||||||
|
if (stdout_data)
|
||||||
|
fd = stderr;
|
||||||
|
else
|
||||||
|
fd = stdout;
|
||||||
|
|
||||||
|
if (global_timestamps) {
|
||||||
|
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
|
||||||
|
HANDLE_RC(rc, MSG_LEN, off, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
out:
|
||||||
|
fprintf(fd, "%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return a newly allocated buffer containing the result of the specified
|
||||||
|
* string format arguments */
|
||||||
|
char *__masprintf(const char *func, const char *file, int line, const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
__util_vasprintf(func, file, line, &str, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the internal name of the calling thread */
|
||||||
|
void __set_threadname(const char *func, const char *file, int line,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
__util_vasprintf(func, file, line, &str, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
pthread_setspecific(thread_name_key, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear any previously set thread name */
|
||||||
|
void clear_threadname(void)
|
||||||
|
{
|
||||||
|
void *addr = pthread_getspecific(thread_name_key);
|
||||||
|
|
||||||
|
if (addr) {
|
||||||
|
pthread_setspecific(thread_name_key, NULL);
|
||||||
|
free(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove any number of trailing characters @c in @str */
|
||||||
|
void chomp(char *str, char *c)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
|
||||||
|
for (i = strlen(str) - 1; i >= 0 && strchr(c, str[i]); i--)
|
||||||
|
str[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove any number of leading characters @c in @str */
|
||||||
|
void lchomp(char *str, char *c)
|
||||||
|
{
|
||||||
|
char *from;
|
||||||
|
|
||||||
|
for (from = str; *from && strchr(c, *from); from++)
|
||||||
|
;
|
||||||
|
if (str != from)
|
||||||
|
memmove(str, from, strlen(from) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform a stat on file referenced by either @abs or @rel and @dref. Store
|
||||||
|
* results in @stat and return stat()'s return code. */
|
||||||
|
int stat_file(bool dereference, const char *abs, const char *rel,
|
||||||
|
struct dref *dref, struct stat *st)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (dref) {
|
||||||
|
if (dereference)
|
||||||
|
rc = fstatat(dref->dirfd, rel, st, 0);
|
||||||
|
else
|
||||||
|
rc = fstatat(dref->dirfd, rel, st, AT_SYMLINK_NOFOLLOW);
|
||||||
|
} else {
|
||||||
|
if (dereference)
|
||||||
|
rc = stat(abs, st);
|
||||||
|
else
|
||||||
|
rc = lstat(abs, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill stat buffer @st with dummy values. */
|
||||||
|
void set_dummy_stat(struct stat *st)
|
||||||
|
{
|
||||||
|
/* Fake stat */
|
||||||
|
memset(st, 0, sizeof(struct stat));
|
||||||
|
st->st_mode = S_IRUSR | S_IWUSR | S_IFREG;
|
||||||
|
st->st_uid = geteuid();
|
||||||
|
st->st_gid = getegid();
|
||||||
|
st->st_mtime = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Redirect all output streams to @fd and execute command @CMD */
|
||||||
|
int cmd_child(int fd, char *cmd)
|
||||||
|
{
|
||||||
|
char *argv[] = { "/bin/sh", "-c", NULL, NULL };
|
||||||
|
char *env[] = { NULL };
|
||||||
|
|
||||||
|
argv[2] = cmd;
|
||||||
|
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
|
||||||
|
mwarn("Could not redirect command output");
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
execve("/bin/sh", argv, env);
|
||||||
|
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PIPE_READ 0
|
||||||
|
#define PIPE_WRITE 1
|
||||||
|
|
||||||
|
/* Run command @cmd as a child process and store its PID in @pid_ptr. On
|
||||||
|
* success, return a file descriptor that is an output pipe to the standard
|
||||||
|
* output and standard error streams of the child process. Return %-1 on
|
||||||
|
* error. */
|
||||||
|
int cmd_open(char *cmd, pid_t *pid_ptr)
|
||||||
|
{
|
||||||
|
int pfd[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if (pipe(pfd) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
/* Fork error */
|
||||||
|
close(pfd[PIPE_READ]);
|
||||||
|
close(pfd[PIPE_WRITE]);
|
||||||
|
return -1;
|
||||||
|
} else if (pid == 0) {
|
||||||
|
/* Child process */
|
||||||
|
close(pfd[PIPE_READ]);
|
||||||
|
exit(cmd_child(pfd[PIPE_WRITE], cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parent process */
|
||||||
|
close(pfd[PIPE_WRITE]);
|
||||||
|
|
||||||
|
*pid_ptr = pid;
|
||||||
|
|
||||||
|
return pfd[PIPE_READ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the file descriptor @fd and end the process with PID @pid. When
|
||||||
|
* not %NULL, use @status_ptr to store the resulting process status. */
|
||||||
|
int cmd_close(int fd, pid_t pid, int *status_ptr)
|
||||||
|
{
|
||||||
|
int status, rc = EXIT_OK;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
kill(pid, SIGQUIT);
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
status = -errno;
|
||||||
|
rc = EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
if (status_ptr)
|
||||||
|
*status_ptr = status;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void misc_init(void)
|
||||||
|
{
|
||||||
|
set_timespec(&main_start_ts, 0, 0);
|
||||||
|
pthread_key_create(&thread_name_key, free);
|
||||||
|
set_threadname("main");
|
||||||
|
}
|
||||||
|
|
||||||
|
void misc_cleanup(void)
|
||||||
|
{
|
||||||
|
clear_threadname();
|
||||||
|
pthread_key_delete(thread_name_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_stdout_data(void)
|
||||||
|
{
|
||||||
|
stdout_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool starts_with(const char *str, const char *prefix)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen(prefix);
|
||||||
|
|
||||||
|
if (strncmp(str, prefix, len) == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ends_with(const char *str, const char *suffix)
|
||||||
|
{
|
||||||
|
size_t str_len, s_len;
|
||||||
|
|
||||||
|
str_len = strlen(str);
|
||||||
|
s_len = strlen(suffix);
|
||||||
|
|
||||||
|
if (str_len < s_len)
|
||||||
|
return false;
|
||||||
|
if (strcmp(str + str_len - s_len, suffix) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove subsequent slashes in @str */
|
||||||
|
void remove_double_slashes(char *str)
|
||||||
|
{
|
||||||
|
size_t i, to;
|
||||||
|
char last;
|
||||||
|
|
||||||
|
last = 0;
|
||||||
|
for (i = 0, to = 0; str[i]; i++) {
|
||||||
|
if (last != '/' || str[i] != '/')
|
||||||
|
last = str[to++] = str[i];
|
||||||
|
}
|
||||||
|
str[to] = 0;
|
||||||
|
}
|
||||||
83
dump2tar/src/strarray.c
Normal file
83
dump2tar/src/strarray.c
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* Dynamically growing string arrays
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
#include "strarray.h"
|
||||||
|
|
||||||
|
/* Release resources associated with string array @array */
|
||||||
|
void free_strarray(struct strarray *array)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < array->num; i++)
|
||||||
|
free(array->str[i]);
|
||||||
|
free(array->str);
|
||||||
|
array->str = NULL;
|
||||||
|
array->num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add string @str to string array @array */
|
||||||
|
void add_str_to_strarray(struct strarray *array, const char *str)
|
||||||
|
{
|
||||||
|
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
|
||||||
|
array->str[array->num + 1] = NULL;
|
||||||
|
array->str[array->num] = mstrdup(str);
|
||||||
|
array->num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add string resulting from @fmt and additional arguments to @array */
|
||||||
|
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
util_vasprintf(&str, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
|
||||||
|
array->str[array->num + 1] = NULL;
|
||||||
|
array->str[array->num] = str;
|
||||||
|
array->num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add all lines in file at @filename to @array */
|
||||||
|
int add_file_to_strarray(struct strarray *array, const char *filename)
|
||||||
|
{
|
||||||
|
FILE *fd;
|
||||||
|
char *line = NULL;
|
||||||
|
size_t line_size;
|
||||||
|
int rc = EXIT_OK;
|
||||||
|
|
||||||
|
fd = fopen(filename, "r");
|
||||||
|
if (!fd) {
|
||||||
|
mwarn("%s: Cannot open file", filename);
|
||||||
|
return EXIT_RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!feof(fd) && !ferror(fd)) {
|
||||||
|
if (getline(&line, &line_size, fd) == -1)
|
||||||
|
continue;
|
||||||
|
chomp(line, "\n");
|
||||||
|
add_str_to_strarray(array, line);
|
||||||
|
}
|
||||||
|
if (ferror(fd))
|
||||||
|
rc = EXIT_RUNTIME;
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
fclose(fd);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
272
dump2tar/src/tar.c
Normal file
272
dump2tar/src/tar.c
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* dump2tar - tool to dump files and command output into a tar archive
|
||||||
|
*
|
||||||
|
* TAR file generation
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2016, 2017
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "idcache.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "tar.h"
|
||||||
|
|
||||||
|
#define LONGLINK "././@LongLink"
|
||||||
|
#define TYPE_LONGLINK 'K'
|
||||||
|
#define TYPE_LONGNAME 'L'
|
||||||
|
|
||||||
|
#define BLOCKSIZE 512
|
||||||
|
|
||||||
|
/* Basic TAR header */
|
||||||
|
struct tar_header {
|
||||||
|
char name[100];
|
||||||
|
char mode[8];
|
||||||
|
char uid[8];
|
||||||
|
char gid[8];
|
||||||
|
char size[12];
|
||||||
|
char mtime[12];
|
||||||
|
char chksum[8];
|
||||||
|
char typeflag;
|
||||||
|
char linkname[100];
|
||||||
|
char magic[6];
|
||||||
|
char version[2];
|
||||||
|
char uname[32];
|
||||||
|
char gname[32];
|
||||||
|
char devmajor[8];
|
||||||
|
char devminor[8];
|
||||||
|
char prefix[155];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Store the octal value of @value to at most @len bytes at @dest */
|
||||||
|
static void set_octal(char *dest, size_t len, unsigned long value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dest[len - 1] = 0;
|
||||||
|
for (i = len - 2; i >= 0; i--) {
|
||||||
|
dest[i] = '0' + (value & 7);
|
||||||
|
value >>= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store time @value to at most @len bytes at @dest */
|
||||||
|
static void set_time(char *dest, size_t len, time_t value)
|
||||||
|
{
|
||||||
|
time_t max = (1ULL << (3 * (len - 1))) - 1;
|
||||||
|
|
||||||
|
if (value >= 0 && value <= max) {
|
||||||
|
set_octal(dest, len, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; len > 0; len--) {
|
||||||
|
dest[len - 1] = value & 0xff;
|
||||||
|
value >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0] |= 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_FIELD(obj, name, value) \
|
||||||
|
set_octal((obj)->name, sizeof((obj)->name), (unsigned long) (value))
|
||||||
|
#define SET_TIME_FIELD(obj, name, value) \
|
||||||
|
set_time((obj)->name, sizeof((obj)->name), (time_t) (value))
|
||||||
|
#define SET_STR_FIELD(obj, name, value) \
|
||||||
|
strncpy((obj)->name, (value), sizeof((obj)->name))
|
||||||
|
|
||||||
|
/* Initialize the tar file @header with the provided data */
|
||||||
|
static void init_header(struct tar_header *header, const char *filename,
|
||||||
|
const char *link, size_t len, struct stat *stat,
|
||||||
|
char type)
|
||||||
|
{
|
||||||
|
unsigned int i, checksum;
|
||||||
|
unsigned char *c;
|
||||||
|
|
||||||
|
memset(header, 0, sizeof(*header));
|
||||||
|
|
||||||
|
/* Fill in header fields */
|
||||||
|
SET_STR_FIELD(header, name, filename);
|
||||||
|
if (link)
|
||||||
|
SET_STR_FIELD(header, linkname, link);
|
||||||
|
SET_FIELD(header, size, len);
|
||||||
|
if (stat) {
|
||||||
|
SET_FIELD(header, mode, stat->st_mode & 07777);
|
||||||
|
SET_FIELD(header, uid, stat->st_uid);
|
||||||
|
SET_FIELD(header, gid, stat->st_gid);
|
||||||
|
SET_TIME_FIELD(header, mtime, stat->st_mtime);
|
||||||
|
uid_to_name(stat->st_uid, header->uname, sizeof(header->uname));
|
||||||
|
gid_to_name(stat->st_gid, header->gname, sizeof(header->gname));
|
||||||
|
} else {
|
||||||
|
SET_FIELD(header, mode, 0644);
|
||||||
|
SET_FIELD(header, uid, 0);
|
||||||
|
SET_FIELD(header, gid, 0);
|
||||||
|
SET_TIME_FIELD(header, mtime, 0);
|
||||||
|
uid_to_name(0, header->uname, sizeof(header->uname));
|
||||||
|
gid_to_name(0, header->gname, sizeof(header->gname));
|
||||||
|
}
|
||||||
|
header->typeflag = type;
|
||||||
|
memcpy(header->magic, "ustar ", sizeof(header->magic));
|
||||||
|
memcpy(header->version, " ", sizeof(header->version));
|
||||||
|
|
||||||
|
/* Calculate checksum */
|
||||||
|
memset(header->chksum, ' ', sizeof(header->chksum));
|
||||||
|
checksum = 0;
|
||||||
|
c = (unsigned char *) header;
|
||||||
|
for (i = 0; i < sizeof(*header); i++)
|
||||||
|
checksum += c[i];
|
||||||
|
snprintf(header->chksum, 7, "%06o", checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit zero bytes via @emit_cb to pad @len to a multiple of BLOCKSIZE */
|
||||||
|
static int emit_padding(emit_cb_t emit_cb, void *data, size_t len)
|
||||||
|
{
|
||||||
|
size_t pad = BLOCKSIZE - len % BLOCKSIZE;
|
||||||
|
char zeroes[BLOCKSIZE];
|
||||||
|
|
||||||
|
if (len % BLOCKSIZE > 0) {
|
||||||
|
memset(zeroes, 0, BLOCKSIZE);
|
||||||
|
return emit_cb(data, zeroes, pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit @len bytes at @addr via @emit_cb and pad data to BLOCKSIZE with zero
|
||||||
|
* bytes */
|
||||||
|
static int emit_data(emit_cb_t emit_cb, void *data, void *addr, size_t len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rc = emit_cb(data, addr, len);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
return emit_padding(emit_cb, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit a tar header via @emit_cb */
|
||||||
|
static int emit_header(emit_cb_t emit_cb, void *data, char *filename,
|
||||||
|
char *link, size_t len, struct stat *stat, char type)
|
||||||
|
{
|
||||||
|
struct tar_header header;
|
||||||
|
size_t namelen = strlen(filename);
|
||||||
|
size_t linklen;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* /proc can contain unreadable links which causes tar to complain
|
||||||
|
* during extract - use a dummy value to handle this more gracefully */
|
||||||
|
if (link && !*link)
|
||||||
|
link = " ";
|
||||||
|
|
||||||
|
linklen = link ? strlen(link) : 0;
|
||||||
|
if (linklen > sizeof(header.linkname)) {
|
||||||
|
rc = emit_header(emit_cb, data, LONGLINK, NULL, linklen + 1,
|
||||||
|
NULL, TYPE_LONGLINK);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
rc = emit_data(emit_cb, data, link, linklen + 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (namelen > sizeof(header.name)) {
|
||||||
|
rc = emit_header(emit_cb, data, LONGLINK, NULL, namelen + 1,
|
||||||
|
NULL, TYPE_LONGNAME);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
rc = emit_data(emit_cb, data, filename, namelen + 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_header(&header, filename, link, len, stat, type);
|
||||||
|
return emit_data(emit_cb, data, &header, sizeof(header));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct emit_content_cb_data {
|
||||||
|
emit_cb_t emit_cb;
|
||||||
|
void *data;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Callback for emitting a single chunk of data of a buffer */
|
||||||
|
static int emit_content_cb(void *data, void *addr, size_t len)
|
||||||
|
{
|
||||||
|
struct emit_content_cb_data *cb_data = data;
|
||||||
|
|
||||||
|
if (len > cb_data->len)
|
||||||
|
len = cb_data->len;
|
||||||
|
cb_data->len -= len;
|
||||||
|
|
||||||
|
cb_data->rc = cb_data->emit_cb(cb_data->data, addr, len);
|
||||||
|
|
||||||
|
if (cb_data->rc || cb_data->len == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit at most @len bytes of contents of @buffer via @emit_cb and pad output
|
||||||
|
* to BLOCKSIZE with zero bytes */
|
||||||
|
static int emit_content(emit_cb_t emit_cb, void *data, struct buffer *buffer,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct emit_content_cb_data cb_data;
|
||||||
|
|
||||||
|
cb_data.emit_cb = emit_cb;
|
||||||
|
cb_data.data = data;
|
||||||
|
cb_data.len = len;
|
||||||
|
cb_data.rc = 0;
|
||||||
|
buffer_iterate(buffer, emit_content_cb, &cb_data);
|
||||||
|
if (cb_data.rc)
|
||||||
|
return cb_data.rc;
|
||||||
|
|
||||||
|
return emit_padding(emit_cb, data, buffer->total);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert file meta data and content specified as @content into a
|
||||||
|
* stream of bytes that is reported via the @emit_cb callback. @data is
|
||||||
|
* passed through to the callback for arbitrary use. */
|
||||||
|
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
|
||||||
|
struct stat *stat, char type,
|
||||||
|
struct buffer *content, emit_cb_t emit_cb,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
|
||||||
|
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
if (content)
|
||||||
|
rc = emit_content(emit_cb, data, content, len);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert file meta data and content specified as @addr and @len into a
|
||||||
|
* stream of bytes that is reported via the @emit_cb callback. @data is
|
||||||
|
* passed through to the callback for arbitrary use. */
|
||||||
|
int tar_emit_file_from_data(char *filename, char *link, size_t len,
|
||||||
|
struct stat *stat, char type, void *addr,
|
||||||
|
emit_cb_t emit_cb, void *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
|
||||||
|
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
if (addr)
|
||||||
|
rc = emit_data(emit_cb, data, addr, len);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
166
etc/cpuplugd.conf
Normal file
166
etc/cpuplugd.conf
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#
|
||||||
|
# Exemplary configuration file for the Linux on System z CPU and memory hotplug
|
||||||
|
# daemon
|
||||||
|
#
|
||||||
|
# The file is evaluated by cpuplugd at startup when called with -c.
|
||||||
|
# It does not contain shell environment variables.
|
||||||
|
|
||||||
|
## Type: integer
|
||||||
|
## Default: 1
|
||||||
|
#
|
||||||
|
# The minimum number of CPUs must be > 0.
|
||||||
|
#
|
||||||
|
CPU_MIN="1"
|
||||||
|
|
||||||
|
## Type: integer
|
||||||
|
## Default: 0
|
||||||
|
#
|
||||||
|
# The maximum number of CPUs to be enabled. If 0 is specified here,
|
||||||
|
# the maximum number of CPUs equals the number of CPUs detected.
|
||||||
|
#
|
||||||
|
CPU_MAX="0"
|
||||||
|
|
||||||
|
## Type: integer
|
||||||
|
## Default: 5
|
||||||
|
#
|
||||||
|
# The update interval described how often the current system state
|
||||||
|
# is checked against the configured set of hotplug and hotunplug rules.
|
||||||
|
# The update interval is defined in seconds.
|
||||||
|
#
|
||||||
|
UPDATE="1"
|
||||||
|
|
||||||
|
## Type: integer
|
||||||
|
## Default: 0
|
||||||
|
#
|
||||||
|
# The minimum size of the static page pool (in 4K pages)
|
||||||
|
#
|
||||||
|
CMM_MIN="0"
|
||||||
|
|
||||||
|
## Type: integer
|
||||||
|
## Default: 131072
|
||||||
|
#
|
||||||
|
# The maximum size of the static page pool (in 4K pages)
|
||||||
|
#
|
||||||
|
# Recommended setting is system size minus 256 MB
|
||||||
|
CMM_MAX="131072" # 512 MB
|
||||||
|
|
||||||
|
#
|
||||||
|
# Variables
|
||||||
|
#
|
||||||
|
# User-defined variables are case-sensitive and must not match a pre-defined
|
||||||
|
# variable or keyword. In the configuration file, definitions
|
||||||
|
# for user-defined variables must precede their use in expressions.
|
||||||
|
# Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and
|
||||||
|
# the "_" character. The maximum name length for a variable is 128 characters,
|
||||||
|
# and the maximum total size for user-defined variables (names + values) is 4096
|
||||||
|
# characters.
|
||||||
|
#
|
||||||
|
pgscan_d="vmstat.pgscan_direct_dma[0] + vmstat.pgscan_direct_normal[0] + vmstat.pgscan_direct_movable[0]"
|
||||||
|
pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]"
|
||||||
|
# page scan rate in pages / timer tick
|
||||||
|
pgscanrate="(pgscan_d - pgscan_d1) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
|
||||||
|
# cache usage in kilobytes
|
||||||
|
avail_cache="meminfo.Cached - meminfo.Shmem"
|
||||||
|
|
||||||
|
user_0="(cpustat.user[0] - cpustat.user[1])"
|
||||||
|
nice_0="(cpustat.nice[0] - cpustat.nice[1])"
|
||||||
|
system_0="(cpustat.system[0] - cpustat.system[1])"
|
||||||
|
user_2="(cpustat.user[2] - cpustat.user[3])"
|
||||||
|
nice_2="(cpustat.nice[2] - cpustat.nice[3])"
|
||||||
|
system_2="(cpustat.system[2] - cpustat.system[3])"
|
||||||
|
CP_Active0="(user_0 + nice_0 + system_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
|
||||||
|
CP_Active2="(user_2 + nice_2 + system_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])"
|
||||||
|
|
||||||
|
CP_ActiveAVG="(CP_Active0+CP_Active2) / 2"
|
||||||
|
idle_0="(cpustat.idle[0] - cpustat.idle[1])"
|
||||||
|
iowait_0="(cpustat.iowait[0] - cpustat.iowait[1])"
|
||||||
|
idle_2="(cpustat.idle[2] - cpustat.idle[3])"
|
||||||
|
iowait_2="(cpustat.iowait[2] - cpustat.iowait[3])"
|
||||||
|
CP_idle0="(idle_0 + iowait_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
|
||||||
|
CP_idle2="(idle_2 + iowait_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])"
|
||||||
|
CP_idleAVG="(CP_idle0 + CP_idle2) / 2"
|
||||||
|
|
||||||
|
## Type: string
|
||||||
|
## Default: "(meminfo.MemFree + cache) / 40"
|
||||||
|
#
|
||||||
|
# The amount of pages the static page pool
|
||||||
|
# is increased if a memunplug rule is matched.
|
||||||
|
#
|
||||||
|
# cmm_inc: 10% of free memory, in 4K pages
|
||||||
|
CMM_INC="meminfo.MemFree / 40"
|
||||||
|
|
||||||
|
## Type: string
|
||||||
|
## Default: "meminfo.MemTotal / 40"
|
||||||
|
#
|
||||||
|
# The amount of pages the static page pool
|
||||||
|
# is decreased if a memplug rule is matched.
|
||||||
|
#
|
||||||
|
# cmm_dec: 10% of total memory, in 4K pages
|
||||||
|
CMM_DEC="meminfo.MemTotal / 40"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ruledefinitions
|
||||||
|
#
|
||||||
|
# Four kinds of rules are distinguished
|
||||||
|
# (1) hotplug rules, used to enable CPUs
|
||||||
|
# (2) hotunplug rules, to disable CPUs
|
||||||
|
# (3) memplug rules, used to increase memory (decrease cmm balloon)
|
||||||
|
# (4) memunplug rules, used to decrease memory (increase cmm balloon)
|
||||||
|
#
|
||||||
|
# Within the hotplug/hotunplug rule definitions the following variables
|
||||||
|
# can be used:
|
||||||
|
# - loadavg: the current loadaverage
|
||||||
|
# - onumcpus: the current number of CPUs which are online
|
||||||
|
# - runnable_proc: the current amount of runnable processes
|
||||||
|
# - user: the current user percentage
|
||||||
|
# - nice: the current nice percentage
|
||||||
|
# - system: the current system percentage
|
||||||
|
# - idle: the current idle percentage
|
||||||
|
# - iowait: the current iowait percentage
|
||||||
|
# - irq: the current irq percentage
|
||||||
|
# - softirq: the current softirq percentage
|
||||||
|
# - steal: the current steal percentage
|
||||||
|
# - guest: the current guest percentage
|
||||||
|
# - guest_nice: the current guest_nice percentage
|
||||||
|
# - cpustat.<name>: data from /proc/stat and /proc/loadavg
|
||||||
|
# - time: floating point timestamp in "seconds.microseconds"
|
||||||
|
# since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC))
|
||||||
|
#
|
||||||
|
## Type: string
|
||||||
|
## Default: "(loadavg > onumcpus + 0.75) & (idle < 10.0)"
|
||||||
|
#
|
||||||
|
HOTPLUG="((1 - CP_ActiveAVG) * onumcpus) < 0.08"
|
||||||
|
|
||||||
|
## Type: string
|
||||||
|
## Default: "(loadavg < onumcpus - 0.25) | (idle > 50)"
|
||||||
|
#
|
||||||
|
HOTUNPLUG="(CP_idleAVG * onumcpus) > 1.15"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Memplug and memunplug can contain the following keywords:
|
||||||
|
# - apcr: the amount of page cache operations,
|
||||||
|
# i.e. pgpin + pgpout from /proc/vmstat
|
||||||
|
# (in 512 byte blocks / second)
|
||||||
|
# - freemem: the amount of free memory (in megabytes)
|
||||||
|
# - swaprate: the number of swap operations, i.e. pswpin + pswpout
|
||||||
|
# from /proc/vmstat (in pages / second)
|
||||||
|
# - meminfo.<name>: any value from /proc/meminfo
|
||||||
|
# - vmstat.<name>: any value from /proc/vmstat
|
||||||
|
# - time: floating point timestamp in "seconds.microseconds"
|
||||||
|
# since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC))
|
||||||
|
#
|
||||||
|
# This function is disabled by default, because this rule has to be
|
||||||
|
# adjusted for each production system, depending on the environment.
|
||||||
|
#
|
||||||
|
## Type: string
|
||||||
|
## Default: "0"
|
||||||
|
#
|
||||||
|
#MEMPLUG="pgscanrate > 20"
|
||||||
|
MEMPLUG="0"
|
||||||
|
|
||||||
|
## Type: string
|
||||||
|
## Default: "0"
|
||||||
|
#
|
||||||
|
#MEMUNPLUG="(meminfo.MemFree + avail_cache) > (meminfo.MemTotal / 10)"
|
||||||
|
MEMUNPLUG="0"
|
||||||
|
|
||||||
89
etc/init.d/cpacfstatsd
Executable file
89
etc/init.d/cpacfstatsd
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: cpacfstatsd
|
||||||
|
# Required-Start: $local_fs
|
||||||
|
# Required-Stop: $local_fs
|
||||||
|
# Should-Start:
|
||||||
|
# Should-Stop:
|
||||||
|
# Default-Start: 2 3 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Start the cpacfstatsd daemon for Linux on System z
|
||||||
|
# Description: CPACF statistics collection daemon process for Linux on System z
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
DAEMON=cpacfstatsd
|
||||||
|
DAEMON_PATH=/usr/sbin/cpacfstatsd
|
||||||
|
RUN_PID_FILE=/var/run/cpacfstatsd.pid
|
||||||
|
RETVAL=0
|
||||||
|
OPTIONS=""
|
||||||
|
|
||||||
|
# source function library
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
if [ ! -f $RUN_PID_FILE ]; then
|
||||||
|
echo -n $"Starting $DAEMON:"
|
||||||
|
$DAEMON_PATH $OPTIONS
|
||||||
|
if [ $? == "0" ]; then
|
||||||
|
log_success_msg
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
echo -n $"Stopping $DAEMON:"
|
||||||
|
if [ -f $RUN_PID_FILE ]; then
|
||||||
|
killproc $DAEMON_PATH -TERM
|
||||||
|
log_success_msg
|
||||||
|
rm -f $RUN_PID_FILE
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
sleep 1
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
status()
|
||||||
|
{
|
||||||
|
if [ ! -f $RUN_PID_FILE ]; then
|
||||||
|
echo "$DAEMON is not running."
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is running."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# How are we called?
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $DAEMON {start|stop|status|restart}"
|
||||||
|
RETVAL=1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
||||||
116
etc/init.d/cpuplugd
Executable file
116
etc/init.d/cpuplugd
Executable file
@@ -0,0 +1,116 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: cpuplugd
|
||||||
|
# Required-Start: $local_fs $remote_fs
|
||||||
|
# Required-Stop: $local_fs $remote_fs
|
||||||
|
# Should-Start:
|
||||||
|
# Should-Stop:
|
||||||
|
# Default-Start: 2 3 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Start the cpu hotplug daemon for Linux on System z
|
||||||
|
# Description: Starts the cpuplugd. It uses the configuration
|
||||||
|
# file /etc/cpuplugd.conf
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# chkconfig: 235 01 99
|
||||||
|
|
||||||
|
DAEMON=cpuplugd
|
||||||
|
DAEMON_PATH=/usr/sbin/cpuplugd
|
||||||
|
CONFIG_FILE=/etc/cpuplugd.conf
|
||||||
|
RUN_PID_FILE=/var/run/cpuplugd.pid
|
||||||
|
RETVAL=0
|
||||||
|
OPTIONS="-c $CONFIG_FILE"
|
||||||
|
|
||||||
|
# source function library
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
if [ ! -f $RUN_PID_FILE ]; then
|
||||||
|
echo -n $"Starting $DAEMON:"
|
||||||
|
$DAEMON_PATH $OPTIONS
|
||||||
|
if [ $? == "0" ]; then
|
||||||
|
log_success_msg
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
echo -n $"Stopping $DAEMON:"
|
||||||
|
if [ -f $RUN_PID_FILE ]; then
|
||||||
|
killproc $DAEMON_PATH -TERM
|
||||||
|
log_success_msg
|
||||||
|
rm -f $RUN_PID_FILE
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
#
|
||||||
|
# We have to wait 2-3 seconds here. When the daemon is stopped it takes
|
||||||
|
# the time we sleep to reactivate cpus. If we restart to fast and
|
||||||
|
# cpuplugd wasn't able to restore some settings we may get a undesired
|
||||||
|
# online cpu count after cpuplugd shutdown
|
||||||
|
#
|
||||||
|
sleep 4
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
status()
|
||||||
|
{
|
||||||
|
if [ ! -f $RUN_PID_FILE ]; then
|
||||||
|
echo "$DAEMON is not running."
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo "$DAEMON (pid $(cat $RUN_PID_FILE), options: $OPTIONS) is running."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reload()
|
||||||
|
{
|
||||||
|
echo -n $"Reloading $DAEMON: "
|
||||||
|
if [ -f $RUN_PID_FILE ]; then
|
||||||
|
killproc $DAEMON_PATH -HUP
|
||||||
|
log_success_msg
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
RETVAL=$?
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# How are we called?
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
reload|force-reload)
|
||||||
|
reload
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
|
||||||
|
RETVAL=1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
||||||
578
etc/init.d/dumpconf
Executable file
578
etc/init.d/dumpconf
Executable file
@@ -0,0 +1,578 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: dumpconf
|
||||||
|
# Required-Start: $local_fs
|
||||||
|
# Required-Stop: $local_fs
|
||||||
|
# Should-Start:
|
||||||
|
# Should-Stop:
|
||||||
|
# Default-Start: 0 1 2 3 5 6
|
||||||
|
# Default-Stop:
|
||||||
|
# Short-Description: Configure s390 dump feature
|
||||||
|
# Description: Configures the s390 dump feature. It uses the configuration file
|
||||||
|
# /etc/sysconfig/dumpconf
|
||||||
|
# X-Systemd-RemainAfterExit: true
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# chkconfig: 012356 01 99
|
||||||
|
|
||||||
|
# This script can be either used stand-alone or as System V init script.
|
||||||
|
|
||||||
|
DUMPCONF_BIN=/etc/init.d/dumpconf
|
||||||
|
DUMP_CONFIG_FILE=/etc/sysconfig/dumpconf
|
||||||
|
CMDFULL=$0
|
||||||
|
CMD="dumpconf"
|
||||||
|
LOCKFILE=/var/lock/$CMD
|
||||||
|
PIDFILE=/var/run/$CMD.pid
|
||||||
|
ERRMSG="Check $DUMP_CONFIG_FILE!"
|
||||||
|
|
||||||
|
RETVAL=0
|
||||||
|
BACKGROUND=0
|
||||||
|
|
||||||
|
pr_info()
|
||||||
|
{
|
||||||
|
if [ $BACKGROUND -eq 0 ]; then
|
||||||
|
echo "$@"
|
||||||
|
else
|
||||||
|
echo "$@" | logger -t dumpconf
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_error()
|
||||||
|
{
|
||||||
|
if [ $BACKGROUND -eq 0 ]; then
|
||||||
|
echo "$@" >&2
|
||||||
|
else
|
||||||
|
echo "$@" | logger -t dumpconf
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_environment()
|
||||||
|
{
|
||||||
|
if [ ! -f $DUMP_CONFIG_FILE ]; then
|
||||||
|
pr_error "no config file found: $DUMP_CONFIG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then
|
||||||
|
pr_error "no sysfs found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
|
||||||
|
if [ "$SYSFSDIR" = "" ]; then
|
||||||
|
pr_error "sysfs not mounted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DUMP_CONFIG_DIR=/$SYSFSDIR/firmware/dump
|
||||||
|
ON_PANIC_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
|
||||||
|
ions/on_panic
|
||||||
|
ON_RESTART_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
|
||||||
|
ions/on_restart
|
||||||
|
if [ ! -d $DUMP_CONFIG_DIR ]; then
|
||||||
|
pr_info "kernel has no dump on panic support"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
REIPL_CONFIG_DIR=/$SYSFSDIR/firmware/reipl
|
||||||
|
if [ ! -d $REIPL_CONFIG_DIR ]; then
|
||||||
|
pr_info "kernel has no dump on panic support"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
VMCMD_CONFIG_DIR=/$SYSFSDIR/firmware/vmcmd
|
||||||
|
|
||||||
|
. $DUMP_CONFIG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
printhelp()
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
Usage: dumpconf [OPTIONS]
|
||||||
|
|
||||||
|
This script can be used to configure the dump device which is used by the
|
||||||
|
Linux kernel in case of a kernel panic.
|
||||||
|
|
||||||
|
It uses the configuration file /etc/sysconfig/dumpconf as input.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-h, --help print this help
|
||||||
|
-v, --version print version information
|
||||||
|
start enable configuration defined in /etc/sysconfig/dumpconf
|
||||||
|
stop disable dump on panic
|
||||||
|
status show current dump on panic configuration
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
printversion()
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
dumpconf: zSeries dump configuration script version 1.1
|
||||||
|
Copyright IBM Corp. 2006, 2009
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
print_invalid_option()
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
dumpconf: invalid option -- $1
|
||||||
|
Try 'dumpconf --help' for more information.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_pidfile()
|
||||||
|
{
|
||||||
|
if [ $(ps $1 | grep $CMD | wc -l) -eq 0 ]; then
|
||||||
|
rm -f $PIDFILE
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_stop_request()
|
||||||
|
{
|
||||||
|
rm -f $PIDFILE 2>/dev/null
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_activation()
|
||||||
|
{
|
||||||
|
# Open lock file with file descriptor 123
|
||||||
|
exec 123>$LOCKFILE
|
||||||
|
if flock -n -x 123; then
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
# concurrent process was faster
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
trap handle_stop_request TERM
|
||||||
|
echo $$ > $PIDFILE
|
||||||
|
else
|
||||||
|
# Nothing to do, "dumpconf start" is already in progress
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Close file descriptor 123
|
||||||
|
exec 123>&-
|
||||||
|
# Do multiple sleeps in order to be interruptible
|
||||||
|
for ((i=0; i < $DELAY_MINUTES * 60; i++)); do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
rm -f $PIDFILE
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: dump device bus id (e.g. 0.0.4711)
|
||||||
|
verify_ccw_dump_device()
|
||||||
|
{
|
||||||
|
line=$(lsdasd -c $1)
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
line=$(lsdasd $1)
|
||||||
|
fi
|
||||||
|
if [ "$line" == "" ]; then
|
||||||
|
pr_info "WARNING: device $1 not found!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
found=false
|
||||||
|
for i in $line
|
||||||
|
do
|
||||||
|
if [ $found == true ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ "$i" == "is" ]; then
|
||||||
|
found=true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
zgetdump -d /dev/$i > /dev/null 2>&1
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
pr_info "WARNING: $1 is no valid dump device!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Helper function to check a device string.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
function CheckDeviceString() {
|
||||||
|
local X
|
||||||
|
|
||||||
|
X=$(
|
||||||
|
echo "$1" |
|
||||||
|
awk --posix -F. '
|
||||||
|
function PrintBusID(css, grp, devno) {
|
||||||
|
while(length(devno) < 4)
|
||||||
|
devno = "0" devno
|
||||||
|
print css "." grp "." devno
|
||||||
|
}
|
||||||
|
NF == 1 && $1 ~ /^[0-9a-fA-F]{1,4}$/ {
|
||||||
|
PrintBusID("0","0", $1)
|
||||||
|
next
|
||||||
|
}
|
||||||
|
NF != 3 || $1 !~ /^[0-9a-fA-F]{1,2}$/ {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
$2 !~ /^[0-9a-fA-F]{1,2}$/ {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
$3 !~ /^[0-9a-fA-F]{1,4}$/ {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PrintBusID($1, $2, $3)
|
||||||
|
}
|
||||||
|
'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$X" != "" ]; then
|
||||||
|
echo $X
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_device()
|
||||||
|
{
|
||||||
|
DEV="$(CheckDeviceString $DEVICE)"
|
||||||
|
if [ "$DEV" != "" ]; then
|
||||||
|
echo $DEV > $1/$2/device
|
||||||
|
else
|
||||||
|
RETVAL=1
|
||||||
|
pr_error "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ $2 == "fcp" ]; then
|
||||||
|
echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
pr_error "ERROR: Invalid WWPN '$WWPN'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
pr_error "ERROR: Invalid LUN '$LUN'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_nss_device()
|
||||||
|
{
|
||||||
|
echo $NSS_NAME > $1/nss/name || RETVAL=1
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_reipl()
|
||||||
|
{
|
||||||
|
if [ "$REIPL_TYPE" == "" ]; then
|
||||||
|
pr_info "reipl on panic configured: Using default reipl values."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$REIPL_TYPE" == "ccw" ] || [ "$REIPL_TYPE" == "fcp" ]; then
|
||||||
|
setup_device $REIPL_CONFIG_DIR $REIPL_TYPE
|
||||||
|
elif [ "$REIPL_TYPE" == "nss" ]; then
|
||||||
|
setup_nss_device $REIPL_CONFIG_DIR
|
||||||
|
else
|
||||||
|
pr_error "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG
|
||||||
|
RETVAL=1
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo $REIPL_TYPE > $REIPL_CONFIG_DIR/reipl_type || RETVAL=1
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
pr_info "$REIPL_TYPE reipl device configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_dump()
|
||||||
|
{
|
||||||
|
if [ "$DUMP_TYPE" == "ccw" ] || [ "$DUMP_TYPE" == "fcp" ]; then
|
||||||
|
setup_device $DUMP_CONFIG_DIR $DUMP_TYPE
|
||||||
|
elif [ "$DUMP_TYPE" != "none" ]; then
|
||||||
|
pr_error "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG
|
||||||
|
RETVAL=1
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo $DUMP_TYPE > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
echo none > $DUMP_CONFIG_DIR/dump_type
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
pr_info "$ON_PANIC on panic configured: Using $DUMP_TYPE dump device."
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_on_panic_vmcmd()
|
||||||
|
{
|
||||||
|
for I in "$VMCMD_1" "$VMCMD_2" "$VMCMD_3" "$VMCMD_4" "$VMCMD_5" "$VMCMD_6" "$VMCMD_7" "$VMCMD_8";
|
||||||
|
do
|
||||||
|
if [ "$I" != "" ]; then
|
||||||
|
if [ "$VMCMD" != "" ]; then
|
||||||
|
VMCMD="$VMCMD\\n$I"
|
||||||
|
else
|
||||||
|
VMCMD=$I
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ ! -d $VMCMD_CONFIG_DIR ]; then
|
||||||
|
pr_error "ERROR: No vmcmd support. Are you running on LPAR?"
|
||||||
|
RETVAL=1
|
||||||
|
elif [ "$VMCMD" == "" ]; then
|
||||||
|
pr_error "ERROR: No VMCMD_x keyword specified." $ERRMSG
|
||||||
|
RETVAL=1
|
||||||
|
else
|
||||||
|
echo -en "$VMCMD" | cat > $VMCMD_CONFIG_DIR/on_panic || RETVAL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 0 ]; then
|
||||||
|
pr_info "vmcmd on panic configured:"
|
||||||
|
pr_info -e "$VMCMD"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_fcp_device()
|
||||||
|
{
|
||||||
|
DEVICE=$(cat $1/fcp/device) || RETVAL=1
|
||||||
|
pr_info "device..: $DEVICE"
|
||||||
|
WWPN=$(cat $1/fcp/wwpn) || RETVAL=1
|
||||||
|
pr_info "wwpn....: $WWPN"
|
||||||
|
LUN=$(cat $1/fcp/lun) || RETVAL=1
|
||||||
|
pr_info "lun.....: $LUN"
|
||||||
|
BOOTPROG=$(cat $1/fcp/bootprog) || RETVAL=1
|
||||||
|
pr_info "bootprog: $BOOTPROG"
|
||||||
|
BR_LBA=$(cat $1/fcp/br_lba) || RETVAL=1
|
||||||
|
pr_info "br_lba..: $BR_LBA"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_ccw_device()
|
||||||
|
{
|
||||||
|
DEVICE=$(cat $1/ccw/device) || RETVAL=1
|
||||||
|
pr_info "device..: $DEVICE"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_nss_name()
|
||||||
|
{
|
||||||
|
NAME=$(cat $1/nss/device) || RETVAL=1
|
||||||
|
pr_info "device..: $NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
status_dump()
|
||||||
|
{
|
||||||
|
CONF_DUMP_TYPE=$(cat $DUMP_CONFIG_DIR/dump_type) || RETVAL=1
|
||||||
|
if [ "$CONF_DUMP_TYPE" == "none" ]; then
|
||||||
|
pr_info "type....: no dump device configured"
|
||||||
|
elif [ "$CONF_DUMP_TYPE" == "ccw" ]; then
|
||||||
|
pr_info "type....: ccw"
|
||||||
|
print_ccw_device $DUMP_CONFIG_DIR
|
||||||
|
verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device)
|
||||||
|
elif [ "$CONF_DUMP_TYPE" == "fcp" ]; then
|
||||||
|
pr_info "type....: fcp"
|
||||||
|
print_fcp_device $DUMP_CONFIG_DIR
|
||||||
|
else
|
||||||
|
pr_error "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!"
|
||||||
|
pr_error " Please check if you have the latest dumpconf package!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_reipl()
|
||||||
|
{
|
||||||
|
REIPL_TYPE=$(cat $REIPL_CONFIG_DIR/reipl_type) || RETVAL=1
|
||||||
|
pr_info "type....: $REIPL_TYPE"
|
||||||
|
if [ "$REIPL_TYPE" == "ccw" ]; then
|
||||||
|
print_ccw_device $REIPL_CONFIG_DIR
|
||||||
|
elif [ "$REIPL_TYPE" == "fcp" ]; then
|
||||||
|
print_fcp_device $REIPL_CONFIG_DIR
|
||||||
|
elif [ "$REIPL_TYPE" == "nss" ]; then
|
||||||
|
print_nss_name $REIPL_CONFIG_DIR
|
||||||
|
else
|
||||||
|
pr_error "ERROR: Unknown reipl device type '$REIPL_TYPE'!"
|
||||||
|
pr_error " Please check if you have the latest dumpconf package!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_dump_reipl()
|
||||||
|
{
|
||||||
|
pr_info -e "\ndump:"
|
||||||
|
status_dump
|
||||||
|
pr_info -e "\nreipl:"
|
||||||
|
status_reipl
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_vmcmd()
|
||||||
|
{
|
||||||
|
VMCMD=$(cat $VMCMD_CONFIG_DIR/on_panic) || RETVAL=1
|
||||||
|
if [ "$VMCMD" == "" ]; then
|
||||||
|
pr_info "WARNING: No VM command specified!"
|
||||||
|
else
|
||||||
|
pr_info "---------------"
|
||||||
|
pr_info "$VMCMD"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
if [ "$1" == "background" ]; then
|
||||||
|
BACKGROUND=1
|
||||||
|
fi
|
||||||
|
test -n "$DELAY_MINUTES" || DELAY_MINUTES=0
|
||||||
|
test "$DELAY_MINUTES" -ge 0 2>/dev/null || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
pr_error "ERROR: Invalid DELAY_MINUTES parameter" \
|
||||||
|
"'$DELAY_MINUTES'." $ERRMSG
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ "$ON_PANIC" != "stop" -a $DELAY_MINUTES -gt 0 ]; then
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
pr_info "A delayed instance of" $CMD \
|
||||||
|
"is already active."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ $BACKGROUND -eq 1 ]; then
|
||||||
|
delay_activation
|
||||||
|
else
|
||||||
|
pr_info "The activation of dumpconf is being delayed" \
|
||||||
|
"for" $DELAY_MINUTES "minutes"
|
||||||
|
$CMDFULL start background > /dev/null 2>&1 &
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$ON_PANIC" == "" ]; then
|
||||||
|
ON_PANIC="$(cat $ON_PANIC_CONFIG_FILE)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$ON_PANIC" in
|
||||||
|
reipl)
|
||||||
|
setup_reipl
|
||||||
|
;;
|
||||||
|
dump|dump_reipl)
|
||||||
|
setup_dump
|
||||||
|
;;
|
||||||
|
vmcmd)
|
||||||
|
setup_on_panic_vmcmd
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
pr_info "stop on panic configured."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
pr_error "ERROR: Unknown 'on panic'" \
|
||||||
|
"type '$ON_PANIC'." $ERRMSG
|
||||||
|
RETVAL=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f $ON_RESTART_CONFIG_FILE ]; then
|
||||||
|
echo $ON_PANIC > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
|
||||||
|
fi
|
||||||
|
echo $ON_PANIC > $ON_PANIC_CONFIG_FILE 2> /dev/null || RETVAL=1
|
||||||
|
|
||||||
|
# check for errors
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 1 ]; then
|
||||||
|
echo stop > $ON_PANIC_CONFIG_FILE
|
||||||
|
pr_error "ERROR: $ON_PANIC not supported by hardware!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
PID=$(cat $PIDFILE)
|
||||||
|
kill -TERM $PID 2> /dev/null
|
||||||
|
rm -f $PIDFILE
|
||||||
|
fi
|
||||||
|
echo none > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
|
||||||
|
if [ -f $ON_RESTART_CONFIG_FILE ]; then
|
||||||
|
echo stop > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
|
||||||
|
fi
|
||||||
|
echo stop > $ON_PANIC_CONFIG_FILE || RETVAL=1
|
||||||
|
if [ $RETVAL -eq 0 ]; then
|
||||||
|
pr_info "Dump on panic is disabled now"
|
||||||
|
else
|
||||||
|
pr_error "Disabling dump on panic failed"
|
||||||
|
fi
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
status()
|
||||||
|
{
|
||||||
|
ON_PANIC=$(cat $ON_PANIC_CONFIG_FILE) || RETVAL=1
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
pr_info "on_panic: $ON_PANIC - dumpconf activation is being" \
|
||||||
|
"delayed for $DELAY_MINUTES minutes"
|
||||||
|
else
|
||||||
|
pr_info "on_panic: $ON_PANIC"
|
||||||
|
fi
|
||||||
|
case "$ON_PANIC" in
|
||||||
|
vmcmd)
|
||||||
|
status_vmcmd
|
||||||
|
;;
|
||||||
|
reipl)
|
||||||
|
status_reipl
|
||||||
|
;;
|
||||||
|
dump)
|
||||||
|
status_dump
|
||||||
|
;;
|
||||||
|
dump_reipl)
|
||||||
|
status_dump_reipl
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
pr_error "ERROR: Unknown on_panic type '$ON_PANIC'"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printhelp
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-v|--version)
|
||||||
|
printversion
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
check_environment
|
||||||
|
|
||||||
|
# If system crashed, an invalid $PIDFILE might still exist
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
cleanup_pidfile $(cat $PIDFILE)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# See how we were called.
|
||||||
|
case "$1" in
|
||||||
|
restart|reload|force-reload|try-restart)
|
||||||
|
stop
|
||||||
|
DELAY_MINUTES=0
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
start $2
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_invalid_option $1
|
||||||
|
RETVAL=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
||||||
161
etc/init.d/mon_statd
Executable file
161
etc/init.d/mon_statd
Executable file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: mon_statd
|
||||||
|
# Required-Start: $local_fs $remote_fs
|
||||||
|
# Required-Stop: $local_fs $remote_fs
|
||||||
|
# Should-Start:
|
||||||
|
# Should-Stop:
|
||||||
|
# Default-Start: 2 3 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Configure the mon_fsstatd and mon_procd daemons.
|
||||||
|
# Description: Configures the mon_fsstatd and mon_procd daemons. It uses the
|
||||||
|
# configuration file /etc/sysconfig/mon_statd.
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# chkconfig: 235 01 99
|
||||||
|
|
||||||
|
DAEMON=mon_statd
|
||||||
|
FSSTATD=mon_fsstatd
|
||||||
|
PROCD=mon_procd
|
||||||
|
FSSTATD_PATH=/usr/sbin/$FSSTATD
|
||||||
|
PROCD_PATH=/usr/sbin/$PROCD
|
||||||
|
CONFIG_FILE=/etc/sysconfig/$DAEMON
|
||||||
|
FSSTATD_PID_FILE=/var/run/$FSSTATD.pid
|
||||||
|
PROCD_PID_FILE=/var/run/$PROCD.pid
|
||||||
|
|
||||||
|
# source function library
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
# Source config file
|
||||||
|
if [ -f $CONFIG_FILE ]; then
|
||||||
|
. $CONFIG_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
UDEVSETTLE=/sbin/udevadm
|
||||||
|
if [ ! -e $UDEVSETTLE ]
|
||||||
|
then
|
||||||
|
UDEVSETTLE=/sbin/udevsettle
|
||||||
|
UDEVSETTLE_CALL="$UDEVSETTLE --timeout=10"
|
||||||
|
else
|
||||||
|
UDEVSETTLE_CALL="$UDEVSETTLE settle --timeout=10"
|
||||||
|
fi
|
||||||
|
|
||||||
|
load_kernel_module()
|
||||||
|
{
|
||||||
|
if [ ! -e /dev/monwriter ]; then
|
||||||
|
echo "Loading monwriter module..."
|
||||||
|
modprobe monwriter 2>&1
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -e $UDEVSETTLE ]
|
||||||
|
then
|
||||||
|
$UDEVSETTLE_CALL
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_daemon()
|
||||||
|
{
|
||||||
|
local daemon_name=$1
|
||||||
|
local daemon_interval=$2
|
||||||
|
local daemon_pid_file=$3
|
||||||
|
local daemon_path=$4
|
||||||
|
|
||||||
|
if [ ! -f $daemon_pid_file ]; then
|
||||||
|
load_kernel_module
|
||||||
|
echo -n "Starting $daemon_name:"
|
||||||
|
$daemon_path -i $daemon_interval
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log_success_msg
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$daemon_name (pid $(cat $daemon_pid_file)) is already running..."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
if [ "$FSSTAT" = "yes" ]; then
|
||||||
|
start_daemon $FSSTATD $FSSTAT_INTERVAL $FSSTATD_PID_FILE \
|
||||||
|
$FSSTATD_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$PROC" = "yes" ]; then
|
||||||
|
start_daemon $PROCD $PROC_INTERVAL $PROCD_PID_FILE \
|
||||||
|
$PROCD_PATH
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_daemon()
|
||||||
|
{
|
||||||
|
local daemon_name=$1
|
||||||
|
local daemon_pid_file=$2
|
||||||
|
local daemon_path=$3
|
||||||
|
|
||||||
|
echo -n "Stopping $daemon_name:"
|
||||||
|
if [ -f $daemon_pid_file ]; then
|
||||||
|
killproc $daemon_path -TERM
|
||||||
|
log_success_msg
|
||||||
|
rm -f $daemon_pid_file
|
||||||
|
else
|
||||||
|
log_failure_msg
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
if [ "$FSSTAT" = "yes" ]; then
|
||||||
|
stop_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTATD_PATH
|
||||||
|
fi
|
||||||
|
if [ "$PROC" = "yes" ]; then
|
||||||
|
stop_daemon $PROCD $PROCD_PID_FILE $PROCD_PATH
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
status_daemon()
|
||||||
|
{
|
||||||
|
local daemon_name=$1
|
||||||
|
local daemon_pid_file=$2
|
||||||
|
local daemon_interval=$3
|
||||||
|
|
||||||
|
if [ ! -f $daemon_pid_file ]; then
|
||||||
|
echo "$daemon_name is not running."
|
||||||
|
else
|
||||||
|
echo "$daemon_name (pid $(cat $daemon_pid_file), interval: $daemon_interval) is running."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status()
|
||||||
|
{
|
||||||
|
status_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTAT_INTERVAL
|
||||||
|
status_daemon $PROCD $PROCD_PID_FILE $PROC_INTERVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
# How are we called?
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
restart|reload|force-reload)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
15
etc/sysconfig/cpi
Normal file
15
etc/sysconfig/cpi
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#
|
||||||
|
# Apply control program identification (CPI) settings
|
||||||
|
#
|
||||||
|
# The system and sysplex names consist of up to eight characters of
|
||||||
|
# the following set: A-Z, 0-9, $, @, #, and blank.
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPI system name
|
||||||
|
#
|
||||||
|
CPI_SYSTEM_NAME=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPI sysplex name
|
||||||
|
#
|
||||||
|
CPI_SYSPLEX_NAME=""
|
||||||
61
etc/sysconfig/dumpconf
Normal file
61
etc/sysconfig/dumpconf
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#
|
||||||
|
# s390 dump config
|
||||||
|
#
|
||||||
|
# Configures the actions which should be performed after a kernel panic
|
||||||
|
# and on PSW restart.
|
||||||
|
#
|
||||||
|
# The following actions are supported:
|
||||||
|
#
|
||||||
|
# * stop: Stop Linux (default)
|
||||||
|
# * dump: Dump Linux with stand-alone dump tool
|
||||||
|
# * vmcmd: Issue z/VM CP commands
|
||||||
|
# * reipl: Re-IPL Linux using setting under /sys/firmware/reipl
|
||||||
|
# * dump_reipl: First dump Linux with stand-alone dump tool, then re-IPL Linux
|
||||||
|
# using setting under /sys/firmware/reipl
|
||||||
|
#
|
||||||
|
|
||||||
|
# For the actions "reipl" and "dump_reipl" the DELAY_MINUTES keyword may
|
||||||
|
# be used to delay the activation of dumpconf.
|
||||||
|
# Thus potential reipl loops caused by kernel panics
|
||||||
|
# which persistently occur early in the boot process can be prevented.
|
||||||
|
|
||||||
|
# Dump on CCW device (DASD) and re-IPL after dump is complete.
|
||||||
|
# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
|
||||||
|
# The activation of dumpconf is delayed by 5 minutes.
|
||||||
|
#
|
||||||
|
# ON_PANIC=dump_reipl
|
||||||
|
# DUMP_TYPE=ccw
|
||||||
|
# DEVICE=0.0.4e13
|
||||||
|
# DELAY_MINUTES=5
|
||||||
|
|
||||||
|
#
|
||||||
|
# Dump on fcp device (SCSI Disk)
|
||||||
|
#
|
||||||
|
# ON_PANIC=dump
|
||||||
|
# DUMP_TYPE=fcp
|
||||||
|
# DEVICE=0.0.4711
|
||||||
|
# WWPN=0x5005076303004711
|
||||||
|
# LUN=0x4711000000000000
|
||||||
|
# BOOTPROG=0
|
||||||
|
# BR_LBA=0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Use VMDUMP
|
||||||
|
#
|
||||||
|
# ON_PANIC=vmcmd
|
||||||
|
# VMCMD_1="MESSAGE * Starting VMDUMP"
|
||||||
|
# VMCMD_2="VMDUMP"
|
||||||
|
# VMCMD_3="IPL 4711"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Stop Linux (default)
|
||||||
|
#
|
||||||
|
# ON_PANIC=stop
|
||||||
|
|
||||||
|
#
|
||||||
|
# Re-IPL Linux
|
||||||
|
# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
|
||||||
|
# Since the DELAY_MINUTES keyword is omitted, there is no delay and
|
||||||
|
# dumpconf becomes active immediately during system startup.
|
||||||
|
#
|
||||||
|
# ON_PANIC=reipl
|
||||||
9
etc/sysconfig/mon_fsstatd
Normal file
9
etc/sysconfig/mon_fsstatd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## Path: System/Monitoring/s390-tools/mon_fsstatd
|
||||||
|
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
|
||||||
|
|
||||||
|
## Type: integer(1:)
|
||||||
|
## Default: 60
|
||||||
|
#
|
||||||
|
# Monitor interval in seconds for filesystem monitoring.
|
||||||
|
#
|
||||||
|
FSSTAT_INTERVAL=60
|
||||||
9
etc/sysconfig/mon_procd
Normal file
9
etc/sysconfig/mon_procd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## Path: System/Monitoring/s390-tools/mon_procd
|
||||||
|
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
|
||||||
|
|
||||||
|
## Type: integer(1:)
|
||||||
|
## Default: 60
|
||||||
|
#
|
||||||
|
# Monitor interval in seconds for process monitoring.
|
||||||
|
#
|
||||||
|
PROC_INTERVAL=60
|
||||||
28
etc/sysconfig/mon_statd
Normal file
28
etc/sysconfig/mon_statd
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
## Path: System/Monitoring/s390-tools/mon_statd
|
||||||
|
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
|
||||||
|
|
||||||
|
## Type: integer(1:)
|
||||||
|
## Default: 60
|
||||||
|
#
|
||||||
|
# Monitor interval in seconds for filesystem monitoring.
|
||||||
|
#
|
||||||
|
FSSTAT_INTERVAL=60
|
||||||
|
|
||||||
|
## Type: integer(1:)
|
||||||
|
## Default: 60
|
||||||
|
#
|
||||||
|
# Monitor interval in seconds for process monitoring.
|
||||||
|
#
|
||||||
|
PROC_INTERVAL=60
|
||||||
|
|
||||||
|
## Type: yesno
|
||||||
|
## Default: no
|
||||||
|
#
|
||||||
|
# Set this to "yes" if you want to enable filesystem monitoring.
|
||||||
|
FSSTAT="no"
|
||||||
|
|
||||||
|
## Type: yesno
|
||||||
|
## Default: no
|
||||||
|
#
|
||||||
|
# Set this to "yes" if you want to enable process monitoring.
|
||||||
|
PROC="no"
|
||||||
6
etc/udev/rules.d/40-z90crypt.rules
Normal file
6
etc/udev/rules.d/40-z90crypt.rules
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
# Rule for z90crypt character device node permissions
|
||||||
|
# This file should be installed in /etc/udev/rules.d
|
||||||
|
#
|
||||||
|
|
||||||
|
KERNEL=="z90crypt", MODE="0666"
|
||||||
1
etc/udev/rules.d/57-osasnmpd.rules
Normal file
1
etc/udev/rules.d/57-osasnmpd.rules
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SUBSYSTEM=="ccwgroup", ACTION=="change", RUN+="/usr/bin/killall -SIGUSR1 osasnmpd"
|
||||||
35
etc/udev/rules.d/59-dasd.rules
Normal file
35
etc/udev/rules.d/59-dasd.rules
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#
|
||||||
|
# Rules for unique DASD device nodes created in /dev/disk/
|
||||||
|
# This file should be installed in /etc/udev/rules.d
|
||||||
|
#
|
||||||
|
|
||||||
|
SUBSYSTEM!="block", GOTO="dasd_symlinks_end"
|
||||||
|
KERNEL!="dasd*", GOTO="dasd_symlinks_end"
|
||||||
|
|
||||||
|
ACTION!="change", GOTO="dasd_block_end"
|
||||||
|
# by-id (hardware serial number)
|
||||||
|
KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/sbin/dasdinfo -a -e -b $kernel"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}"
|
||||||
|
KERNEL=="dasd*[!0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}"
|
||||||
|
|
||||||
|
LABEL="dasd_block_end"
|
||||||
|
|
||||||
|
ACTION!="change|add", GOTO="dasd_symlinks_end"
|
||||||
|
|
||||||
|
# for partitions import parent information
|
||||||
|
KERNEL=="dasd*[0-9]", IMPORT{parent}=="ID_*"
|
||||||
|
|
||||||
|
KERNEL=="dasd*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
|
||||||
|
KERNEL=="dasd*[0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}-part%n"
|
||||||
|
KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}-part%n"
|
||||||
|
|
||||||
|
LABEL="dasd_symlinks_end"
|
||||||
|
|
||||||
|
# on device add set request queue scheduler to deadline
|
||||||
|
SUBSYSTEM!="block", GOTO="sched_end"
|
||||||
|
|
||||||
|
ACTION!="change", GOTO="sched_end"
|
||||||
|
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline"
|
||||||
|
|
||||||
|
LABEL="sched_end"
|
||||||
18
etc/udev/rules.d/60-readahead.rules
Normal file
18
etc/udev/rules.d/60-readahead.rules
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Rules to set an increased default max readahead size
|
||||||
|
# This file should be installed in /etc/udev/rules.d
|
||||||
|
#
|
||||||
|
|
||||||
|
SUBSYSTEM!="block", GOTO="ra_end"
|
||||||
|
|
||||||
|
ACTION!="add", GOTO="ra_change"
|
||||||
|
# on device add set initial readahead to 512 (instead of in kernel 128)
|
||||||
|
KERNEL=="sd*[!0-9]", WAIT_FOR="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512"
|
||||||
|
|
||||||
|
LABEL="ra_change"
|
||||||
|
|
||||||
|
ACTION!="change", GOTO="ra_end"
|
||||||
|
# on device change set initial readahead to 512 (instead of in kernel 128)
|
||||||
|
KERNEL=="dasd*[!0-9]", TEST=="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512"
|
||||||
|
|
||||||
|
LABEL="ra_end"
|
||||||
22
fdasd/Makefile
Normal file
22
fdasd/Makefile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
libs = $(rootdir)/libvtoc/libvtoc.a \
|
||||||
|
$(rootdir)/libzds/libzds.a \
|
||||||
|
$(rootdir)/libdasd/libdasd.a \
|
||||||
|
$(rootdir)/libu2s/libu2s.a \
|
||||||
|
$(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
all: fdasd
|
||||||
|
|
||||||
|
fdasd: fdasd.o $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 fdasd $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 fdasd.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ fdasd core
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
185
fdasd/fdasd.8
Normal file
185
fdasd/fdasd.8
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
.\" Copyright 2017 IBM Corp.
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\"
|
||||||
|
.TH FDASD 8 "Apr 2006" "s390-tools"
|
||||||
|
.SH NAME
|
||||||
|
fdasd \- partitioning tool.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
interactive mode:
|
||||||
|
.br
|
||||||
|
\fBfdasd\fR [-s] [-r] [-C] \fIdevice\fR
|
||||||
|
.br
|
||||||
|
command line mode:
|
||||||
|
.br
|
||||||
|
\fBfdasd\fR [-s] [-r] [-C] {-a[-k|-l \fIvolser\fR]|-i|-p|-c \fIconf_file\fR}
|
||||||
|
[-f \fI[type,blocksize]\fR] \fIdevice\fR
|
||||||
|
.br
|
||||||
|
help:
|
||||||
|
.br
|
||||||
|
\fBfdasd\fR {-h|-v}
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\fBfdasd\fR writes a partition table to a cdl (compatible disk layout)
|
||||||
|
formatted DASD, in the form of
|
||||||
|
a VTOC (volume table of contents) for usage with Linux for S/390
|
||||||
|
or zSeries. If fdasd detects a valid \fBVOL1\fR volume label, it
|
||||||
|
will use it, otherwise it asks to write a new one.
|
||||||
|
.br
|
||||||
|
|
||||||
|
\fBAttention\fR: Careless use of
|
||||||
|
\fBfdasd\fR can result in loss of data.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB-h\fR or \fB--help\fR
|
||||||
|
Print usage information, then exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-v\fR or \fB--version\fR
|
||||||
|
Print version information, then exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-s\fR or \fB--silent\fR
|
||||||
|
Suppress messages in non-interactive mode.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-r\fR or \fB--verbose\fR
|
||||||
|
Provide more verbose output.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-a\fR or \fB--auto\fR
|
||||||
|
Automatically create a partition using the entire disk in non-interactive
|
||||||
|
mode.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-k\fR or \fB--keep_volser\fR
|
||||||
|
Keeps the volume serial when writing the volume label.
|
||||||
|
.br
|
||||||
|
This is useful, if the volume serial has been written before and should not
|
||||||
|
be overwritten. This option is only applicable in non-interactive mode.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-l\fR \fIvolser\fR or \fB--label\fR \fIvolser\fR
|
||||||
|
Specify the volume serial.
|
||||||
|
.br
|
||||||
|
\fIvolser\fR is interpreted as ASCII string and is automatically converted to
|
||||||
|
uppercase, padded with blanks and finally converted to EBCDIC to be written
|
||||||
|
to disk. This option is only applicable in non-interactive mode.
|
||||||
|
.br
|
||||||
|
|
||||||
|
Do not use the following reserved volume serial: SCRTCH, PRIVAT, MIGRAT,
|
||||||
|
or Lnnnnn (L with five digit number); These are used as keywords by
|
||||||
|
other operating systems (OS/390).
|
||||||
|
.br
|
||||||
|
|
||||||
|
A volume serial is 1 through 6 alphanumeric characters or one of the
|
||||||
|
following special characters: $, #, @, %. All other characters are simply
|
||||||
|
ignored.
|
||||||
|
.br
|
||||||
|
Try to avoid using special characters in the volume serial.
|
||||||
|
This may cause problems accessing a disk by volser.
|
||||||
|
In case you really have to use special characters, make sure you are using
|
||||||
|
quotes. In addition there is a special handling for the '$' sign.
|
||||||
|
Please specify it using '\\$' if necessary.
|
||||||
|
.br
|
||||||
|
|
||||||
|
e.g. -l 'a@b\\$c#' to get A@B$C#
|
||||||
|
.br
|
||||||
|
|
||||||
|
Omitting this parameter causes fdasd to ask for it in case it is needed.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-c\fR \fIconf_file\fR or \fB--config\fR \fIconf_file\fR
|
||||||
|
Use this option to create multiple partitions according to
|
||||||
|
specifications in a configuration file, \fIconf_file\fR.
|
||||||
|
.br
|
||||||
|
The configuration file contains one line for each partition. The lines
|
||||||
|
have this format:
|
||||||
|
.sp
|
||||||
|
[<first>,<last>,<type>]
|
||||||
|
.br
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
.br
|
||||||
|
<first> and <last> are numbers that specify the first and the last
|
||||||
|
track of the partition. Instead of a numerical value, you can
|
||||||
|
specify 'first' for the first possible track on the disk and 'last'
|
||||||
|
for the last possible track on disk.
|
||||||
|
.br
|
||||||
|
|
||||||
|
<type> is optional and specifies the partition type. <type> can be one
|
||||||
|
of: native, swap, raid, lvm, or gpfs.
|
||||||
|
If omitted, 'native' is used.
|
||||||
|
.br
|
||||||
|
|
||||||
|
The configuration file of the following example specifies three
|
||||||
|
partitions that use the entire disk:
|
||||||
|
.sp
|
||||||
|
[first,1000]
|
||||||
|
.br
|
||||||
|
[1001,2000,swap]
|
||||||
|
.br
|
||||||
|
[2001,last,lvm]
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-i\fR or \fB--volser\fR
|
||||||
|
Print the volume serial, then exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-p\fR or \fB--table\fR
|
||||||
|
Print partition table, then exit.
|
||||||
|
.br
|
||||||
|
In combination with the -s option fdasd will display a short version of the
|
||||||
|
partition table.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-C\fR or \fB--check_host_count\fR
|
||||||
|
Force fdasd to check the host access open count to ensure the device
|
||||||
|
is not online on another operating system instance
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB-f\fR \fI[type,blocksize]\fR or \fB--force\fR \fI[type,blocksize]\fR
|
||||||
|
Force fdasd to work on non DASD devices.
|
||||||
|
.br
|
||||||
|
If fdasd is to be used on a block device that is neither a native DASD
|
||||||
|
nor exposes the proper disk geometry of a DASD of type 3390,
|
||||||
|
then the --force option can be used to assume the geometry of a
|
||||||
|
given device type. The default device type is 3390 and the default
|
||||||
|
block size is 4096. An optional argument of <device type>,<blocksize>
|
||||||
|
can be used to specify type and blocksize explicitly. For example:
|
||||||
|
|
||||||
|
-f
|
||||||
|
|
||||||
|
has the same effect as
|
||||||
|
|
||||||
|
-f3390,4096 or --force=3390,4096
|
||||||
|
|
||||||
|
Valid device types are: 3390, 3380, 9345
|
||||||
|
.br
|
||||||
|
Valid block sizes are: 4096, 2048, 1024, 512
|
||||||
|
|
||||||
|
You can use the verbose option to get information about the disk
|
||||||
|
geometry that fdasd has computed from device type and block size.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fIdevice\fR
|
||||||
|
This parameter specifies the device to be partitioned:
|
||||||
|
.sp
|
||||||
|
\fB/dev/dasd/\fR\fIxxxx\fR\fB/device\fR
|
||||||
|
.br
|
||||||
|
|
||||||
|
where \fIxxxx\fR is the four-letter devno (device number).
|
||||||
|
.br
|
||||||
|
|
||||||
|
In case your are not using the device file system, please specify:
|
||||||
|
.sp
|
||||||
|
\fB/dev/dasd\fR\fIx\fR
|
||||||
|
.br
|
||||||
|
|
||||||
|
where \fIx\fR is one or more lowercase letter(s) or any other device
|
||||||
|
node specification configured by udev for kernel 2.6 or higher.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dasdfmt (8)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user