apt-dater/0000775000175000017500000000000015131461773010535 5ustar memeapt-dater/man/0000775000175000017500000000000015131461765011311 5ustar memeapt-dater/man/Makefile.am0000664000175000017500000000071215131461765013345 0ustar memeEXTRA_DIST=apt-dater.xml apt-dater.xml.xml HTMLS=apt-dater.html apt-dater.xml.html MAN5S=apt-dater.xml.5 MAN8S=apt-dater.8 man_MANS=$(MAN8S) $(MAN5S) $(HTMLS) CLEANFILES=$(man_MANS) XSLTPROC=@XSLTPROC@ DBXSL=@DBXSL@ $(HTMLS): %.html: %.xml $(XSLTPROC) -o $@ -''-nonet $(DBXSL)/html/docbook.xsl $< $(MAN5S): %.5: %.xml $(XSLTPROC) -''-nonet $(DBXSL)/manpages/docbook.xsl $< $(MAN8S): %.8: %.xml $(XSLTPROC) -''-nonet $(DBXSL)/manpages/docbook.xsl $< apt-dater/man/apt-dater.xml.xml0000664000175000017500000003534315131461765014523 0ustar meme .
will be generated. You may view the manual page with: nroff -man .
| less'. A typical entry in a Makefile or Makefile.am is: DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\ manpages/docbook.xsl XP=xsltproc -''-nonet manpage.1: manpage.dbk $(XP) $(DB2MAN) $< The xsltproc binary is found in the xsltproc package. The XSL files are in docbook-xsl. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include xsltproc and docbook-xsl in your Build-Depends control field. --> Thomas"> Liske"> February 28, 2015"> 5"> liske@ibh.de"> APT-DATER"> Debian"> GNU"> GPL"> ]> apt-dater Thomas Liske liske@ibh.de maintainer 2008-2015 IBH IT-Service GmbH [https://www.ibh.de/] &dhdate; Andre Ellguth ex-maintainer apt-dater Config File Manual &dhucpackage; &dhsection; &dhpackage;.xml configuration file of &dhpackage;(8) ]]> DESCRIPTION The file &dhpackage;.xml is the configuration file for &dhpackage;(8). &dhpackage;(8) is a program to manage package updates on a large number of remote hosts using SSH. This man page descripes briefly the parameters of the file &dhpackage;.xml. The default location of this file is $XDG_CONFIG_HOME/apt-dater/apt-dater.xml. The configuration file is parsed using libxml2 and supports Xincludes. OPTIONS Attributes of the '<symbol>paths</symbol>' element hosts-file="$XDG_CONFIG_HOME/apt-dater/hosts.xml" The location of the file contains all host which will be managed with &dhpackage;. (optional) stats-dir="$XDG_DATA_HOME/apt-dater" The location of the directory which contains the status files of any host. (optional) Attributes of the'<symbol>screen</symbol>' element rc-file="$XDG_CONFIG_HOME/apt-dater/screenrc" Location of screen(1) config file. (optional) title="%m # %u@%h:%p" Template for screen titles using string escapes. (optional) no-dumps="false" Enables/disables support of screen dumps. (optional) query-maintainer="false" If set to 1, apt-dater will ask for the maintainers name on startup. If set to 2, apt-dater will only ask if no MAINTAINER environment variable is set. Attributes of the'<symbol>ssh</symbol>' element cmd="/usr/bin/ssh The location of ssh-client binary. sftp-cmd The command to initiate sftp file transfer e.g. /usr/bin/sftp opt-cmd-flags Additional ssh-client command flags, which will be appended. spawn-agent="false" If enabled, apt-dater tries to spawn ssh-agent(1) if none is already running. ssh-add(1) will be called to load your private keys. add-keys List of private keys which should be loaded when apt-dater calls ssh-add(1). apt-dater will try to load additional private SSH keys using ssh-add(1) during start. Those keys needs to be listed as add-key child elements. The attribute fn refers to the filename of the private key: ]]> Attributes of the'<symbol>commands</symbol>' element CmdRefresh="apt-dater-host refresh" The command which will be used to refresh the status of the managed host. CmdUpgrade="apt-dater-host upgrade" The command which will be used to intitate a upgrade of a managed host. CmdInstall="apt-dater-host install %s" The command which will be used to update a single package. %s will be replaced by the name of the package. Attributes of the'<symbol>appearance</symbol>' element colors Set your own color style of the ui components. To highlight colors you can use the bright* keyword. Example: Colors=menu brightgreen blue;status brightgreen blue;selector black red;. Components are: default menu status selector hoststatus query input Colors are: default black cyan green magenta red white yellow Attributes of the'<symbol>auto-ref</symbol>' element enabled="true" Enabled the auto refresh feature if compiled in (see README.autoref). Attributes of the'<symbol>notify</symbol>' element beep="true" Enables user notification by terminal bell. flash="true" Enables user notification by terminal flashing. Attributes of the'<symbol>history</symbol>' element err-pattern="((?<!no )error|(?<!insserv: )warning|failed|fail(?!2ban))" A regular expression pattern to match the screen output. A match indicates the user should be asked to review the output (using less). (Optional) record="true" Enables session recording using script(1). (Optional) Attributes of the'<symbol>hooks</symbol>' element pre-update="/etc/apt-dater/pre-upg.d" pre-refresh="/etc/apt-dater/pre-ref.d" pre-install="/etc/apt-dater/pre-ins.d" pre-connect="/etc/apt-dater/pre-con.d" post-update="/etc/apt-dater/post-upg.d" post-refresh="/etc/apt-dater/post-ref.d" post-install="/etc/apt-dater/post-ins.d" post-connect="/etc/apt-dater/post-con.d" Hooks to be run before and after an action on a host is done. The values should be path names, any executable script within these directories will be run by run-parts(8). STRING ESCAPES &dhpackage;(8) provides an string escape mechanism. The escape character is '%'. List of supported escapes.
escape replaced by
% escape character
h hostname
H SSH hostname used for connecting
m maintainer name
p SSH port number
u SSH username
U SSH username appended by '@', empty string if no SSH username is configured.
FILES apt-dater.xml The configuration file of apt-dater. hosts.xml Contains all hosts you would like to manage. SEE ALSO apt-dater(8), apt-get(1), debtrack, screen(1), script(1), ssh(1), XDG Base Directory Specification.
apt-dater/man/apt-dater.xml0000664000175000017500000001265115131461765013721 0ustar meme .
will be generated. You may view the manual page with: nroff -man .
| less'. A typical entry in a Makefile or Makefile.am is: DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\ manpages/docbook.xsl XP=xsltproc -''-nonet manpage.1: manpage.dbk $(XP) $(DB2MAN) $< The xsltproc binary is found in the xsltproc package. The XSL files are in docbook-xsl. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include xsltproc and docbook-xsl in your Build-Depends control field. --> Thomas"> Liske"> October 23, 2014"> 8"> liske@ibh.de"> APT-DATER"> Debian"> GNU"> GPL"> ]> apt-dater Thomas Liske liske@ibh.de maintainer 2008-2014 IBH IT-Service GmbH [https://www.ibh.de/] &dhdate; Andre Ellguth ex-maintainer apt-dater apt-dater &dhucpackage; &dhsection; &dhpackage; terminal-based remote package update manager &dhpackage; DESCRIPTION This manual page documents briefly the &dhpackage; command. &dhpackage; is a program to manage package updates on a large number of remote hosts using SSH. OPTIONS &dhpackage; accepts the following command parameters: Use alternative config file. Default is $XDG_CONFIG_HOME/apt-dater/apt-dater.xml. Show apt-dater version and copyright statement. Refresh hosts and create XML report on stdout. apt-dater must be compiled with XMLREPORT feature. Prevent hosts to be refreshed before create XML report (useful for cronjobs which do not have access to SSH key(s)). ENVIRONMENT MAINTAINER The maintainer's name (i.e. used by debtrack). If not set, the GECOS entry for the current user will be used. This environment variable could be forwarded to remote hosts with the ssh(1) SendEnv+AcceptEnv options. FILES ~/.config/apt-dater/apt-dater.xml The configuration file of apt-dater ~/.config/apt-dater/hosts.xml Contains all hosts you would like to manage SEE ALSO apt-dater.xml(5), apt-get(1), debtrack, ssh(1), XDG Base Directory Specification. apt-dater/.travis.yml0000664000175000017500000002326015131461765012652 0ustar memesudo: required language: generic services: - docker env: - features="" - features="--enable-debug" - features="--enable-xmlreport" - features="--enable-autoref" - features="--enable-history" - features="--enable-clusters" - features="--enable-tclfilter" - features="--enable-tmux" - features="--enable-debug --enable-xmlreport" - features="--enable-autoref --enable-debug" - features="--enable-debug --enable-history" - features="--enable-clusters --enable-debug" - features="--enable-debug --enable-tclfilter" - features="--enable-debug --enable-tmux" - features="--enable-autoref --enable-xmlreport" - features="--enable-history --enable-xmlreport" - features="--enable-clusters --enable-xmlreport" - features="--enable-tclfilter --enable-xmlreport" - features="--enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-history" - features="--enable-autoref --enable-clusters" - features="--enable-autoref --enable-tclfilter" - features="--enable-autoref --enable-tmux" - features="--enable-clusters --enable-history" - features="--enable-history --enable-tclfilter" - features="--enable-history --enable-tmux" - features="--enable-clusters --enable-tclfilter" - features="--enable-clusters --enable-tmux" - features="--enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-debug --enable-xmlreport" - features="--enable-debug --enable-history --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-xmlreport" - features="--enable-debug --enable-tclfilter --enable-xmlreport" - features="--enable-debug --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-history" - features="--enable-autoref --enable-clusters --enable-debug" - features="--enable-autoref --enable-debug --enable-tclfilter" - features="--enable-autoref --enable-debug --enable-tmux" - features="--enable-clusters --enable-debug --enable-history" - features="--enable-debug --enable-history --enable-tclfilter" - features="--enable-debug --enable-history --enable-tmux" - features="--enable-clusters --enable-debug --enable-tclfilter" - features="--enable-clusters --enable-debug --enable-tmux" - features="--enable-debug --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-history --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-xmlreport" - features="--enable-autoref --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-history --enable-xmlreport" - features="--enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-history --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-tclfilter --enable-xmlreport" - features="--enable-clusters --enable-tmux --enable-xmlreport" - features="--enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-history" - features="--enable-autoref --enable-history --enable-tclfilter" - features="--enable-autoref --enable-history --enable-tmux" - features="--enable-autoref --enable-clusters --enable-tclfilter" - features="--enable-autoref --enable-clusters --enable-tmux" - features="--enable-autoref --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-history --enable-tclfilter" - features="--enable-clusters --enable-history --enable-tmux" - features="--enable-history --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-debug --enable-history --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-history --enable-xmlreport" - features="--enable-debug --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-debug --enable-history --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-tclfilter --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-tmux --enable-xmlreport" - features="--enable-debug --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-history" - features="--enable-autoref --enable-debug --enable-history --enable-tclfilter" - features="--enable-autoref --enable-debug --enable-history --enable-tmux" - features="--enable-autoref --enable-clusters --enable-debug --enable-tclfilter" - features="--enable-autoref --enable-clusters --enable-debug --enable-tmux" - features="--enable-autoref --enable-debug --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-debug --enable-history --enable-tclfilter" - features="--enable-clusters --enable-debug --enable-history --enable-tmux" - features="--enable-debug --enable-history --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-debug --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-history --enable-xmlreport" - features="--enable-autoref --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-history --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-clusters --enable-history --enable-tmux --enable-xmlreport" - features="--enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-history --enable-tclfilter" - features="--enable-autoref --enable-clusters --enable-history --enable-tmux" - features="--enable-autoref --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-history --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-history --enable-tmux --enable-xmlreport" - features="--enable-debug --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tclfilter" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tmux" - features="--enable-autoref --enable-debug --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-debug --enable-tclfilter --enable-tmux" - features="--enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-history --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-debug --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-tmux" - features="--enable-autoref --enable-clusters --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" - features="--enable-autoref --enable-clusters --enable-debug --enable-history --enable-tclfilter --enable-tmux --enable-xmlreport" before_install: - docker run --name travis-build -e features="$features" -v $(pwd):/prj -it debian /prj/build/travis-before.sh - docker commit travis-build travis-build install: - docker run --name travis-install -e features="$features" -v $(pwd):/prj -it travis-build /prj/build/travis-install.sh - docker commit travis-install travis-install script: - docker run --rm -v $(pwd):/prj -it travis-install /prj/src/apt-dater -v apt-dater/images/0000775000175000017500000000000015131461765012003 5ustar memeapt-dater/images/apt-dater.svg0000664000175000017500000001256315131461765014414 0ustar meme image/svg+xml 0 Hosts in status... q:quit ?:help /:search [ ] Updates pending[ ] Up to date[ ] Status file missing[ ] Refresh required[ ] In refresh[ ] Sessions[ ] Unknown apt-dater/images/apt-dater.xpm0000664000175000017500000001317715131461765014423 0ustar meme/* XPM */ static char *apt-dater[] = { /* columns rows colors chars-per-pixel */ "32 32 220 2", " c black", ". c #010101", "X c #020202", "o c gray1", "O c #040404", "+ c gray3", "@ c #090909", "# c gray4", "$ c #0B0B0B", "% c #0C0C0C", "& c gray5", "* c #0E0E0E", "= c #101010", "- c #111111", "; c gray7", ": c #131313", "> c #151515", ", c #1B1B1B", "< c gray11", "1 c gray12", "2 c #00013F", "3 c #3F011E", "4 c #3D081E", "5 c #3D0A1D", "6 c #3C1F1F", "7 c #36271A", "8 c #39261D", "9 c #202020", "0 c #222222", "q c #232323", "w c #252525", "e c gray15", "r c #272727", "t c #282828", "y c gray16", "u c gray17", "i c #2C2C2C", "p c #2D2D2D", "a c gray18", "s c #2F2F2F", "d c gray19", "f c #313131", "g c #323232", "h c gray20", "j c #353535", "k c gray21", "l c gray22", "z c #3A3A3A", "x c gray23", "c c #3C3C3C", "v c #3F3F3F", "b c #000040", "n c #00067C", "m c #00087B", "M c #00097B", "N c #000A7A", "B c #000B7A", "V c #000C79", "C c #000D79", "Z c #000E78", "A c #001077", "S c #001177", "D c #001276", "F c #001475", "G c #001B72", "H c #001C71", "J c #001D71", "K c #001E70", "L c #00206F", "P c #00216F", "I c #00236E", "U c #00246D", "Y c #00276C", "T c #00296B", "R c #002A6A", "E c #002B6A", "W c #002D69", "Q c #002F68", "! c #003167", "~ c #003266", "^ c #003366", "/ c #003664", "( c #003A62", ") c #003D61", "_ c #00415F", "` c #00475C", "' c #00604F", "] c #00624E", "[ c #00654D", "{ c #00664C", "} c #00684B", "| c #006B4A", " . c #006C49", ".. c #006E48", "X. c #007047", "o. c #007147", "O. c #007545", "+. c #007644", "@. c #007744", "#. c #007943", "$. c #007B42", "%. c #007D41", "&. c #007E40", "*. c #40001F", "=. c #40011F", "-. c #410220", ";. c #420321", ":. c #434343", ">. c #444444", ",. c gray27", "<. c #464646", "1. c gray28", "2. c #484848", "3. c gray29", "4. c #4B4B4B", "5. c #4C4C4C", "6. c #4E4E4E", "7. c #505050", "8. c #515151", "9. c gray33", "0. c gray34", "q. c #585858", "w. c #5A5A5A", "e. c #5B5B5B", "r. c gray36", "t. c #5D5D5D", "y. c gray37", "u. c #5F5F5F", "i. c #606060", "p. c gray38", "a. c #626262", "s. c gray39", "d. c #646464", "f. c #656565", "g. c gray40", "h. c #676767", "j. c DimGray", "k. c #6A6A6A", "l. c #6C6C6C", "z. c gray43", "x. c #6F6F6F", "c. c #717171", "v. c #727272", "b. c gray45", "n. c #747474", "m. c gray46", "M. c #777777", "N. c gray48", "B. c #7C7C7C", "V. c gray50", "C. c navy", "Z. c #00803F", "A. c #00823E", "S. c #00843D", "D. c #00893B", "F. c #008F38", "G. c #009236", "H. c #009B32", "J. c #009C31", "K. c #00A12F", "L. c #00A72C", "P. c #00B027", "I. c #00B127", "U. c #00B923", "Y. c #800000", "T. c #850B0B", "R. c #850C0C", "E. c #942929", "W. c #952C2C", "Q. c #962E2E", "!. c #973030", "~. c #983131", "^. c #993333", "/. c #9A3535", "(. c #9A3636", "). c #9D3B3B", "_. c #9E3D3D", "`. c #A04242", "'. c #A14343", "]. c #A14444", "[. c #A24646", "{. c #A34747", "}. c #A44949", "|. c #A44A4A", " X c #A54B4B", ".X c #A54C4C", "XX c #A74F4F", "oX c #A85151", "OX c #A95353", "+X c #AC5A5A", "@X c #AD5C5C", "#X c #AE5D5D", "$X c #AF6060", "%X c #B26565", "&X c #B36767", "*X c #B46969", "=X c #B56B6B", "-X c #B56C6C", ";X c #B87171", ":X c #B97373", ">X c #BA7676", ",X c #808080", "X+XXXE.'.].=XOXXX:X&X#XXXQ.Y.Y.Y.Y.Y.Y.Y.Y.", "Y.}.R.].W./.uX_.].#X_.'.^.!.#X_.oX^.}.}.].].>X(.Y.Y.Y.Y.Y.Y.Y.Y.", " z & p p p ; f $ ; p % f $ ", " 8. e.e.d.tXq V.8Xr 5XN..l.& ", " 4.% :.p j :.8.z 8.z p p f :.x q k >.k c <.3.s 0X$ ", " z % f z p $ :.; $ O p > O & & $ * + u $ $ q > ", " 6. e.z.k.tXz.M.0Xb.V.1Xu d.wX6X apt-dater/etc/hosts.xml.in0000664000175000017500000000256115131461765013604 0ustar meme apt-dater/schema/0000775000175000017500000000000015131461765011776 5ustar memeapt-dater/schema/report.dtd0000664000175000017500000000220515131461765014005 0ustar meme apt-dater/schema/hosts.dtd0000664000175000017500000000221515131461765013633 0ustar meme apt-dater/schema/Makefile.am0000664000175000017500000000011615131461765014030 0ustar memeschemadir=$(XMLSCHEMADIR) dist_schema_DATA=apt-dater.dtd hosts.dtd report.dtd apt-dater/schema/apt-dater.dtd0000664000175000017500000000453415131461765014362 0ustar meme apt-dater/conf/0000775000175000017500000000000015131461765011463 5ustar memeapt-dater/conf/Makefile.am0000664000175000017500000000035415131461765013521 0ustar memeINCS = apt-dater.xml.inc hosts.xml.inc screenrc.inc tmux.conf.inc EXTRA_DIST = apt-dater.xml hosts.xml screenrc tmux.conf CLEANFILES=$(INCS) all: $(INCS) $(INCS): %.inc: % xxd --name "`basename $<`" -i $< > $@ clean: rm -f *.inc apt-dater/conf/tmux.conf0000664000175000017500000000127715131461765013336 0ustar meme# Use C-a as alternative prefix (like GNU screen) set-option -g prefix2 C-a # Make C-a C-a to send C-a inside bind-key C-a send-prefix -2 # Allow faster key repetition set -s escape-time 0 # Fix scrolling set -g terminal-overrides 'xterm*:smcup@:rmcup@' set-option -g history-limit 10000 set-option -g set-remain-on-exit on # Make C-a q kill the pane (simular to GNU screen) bind-key q confirm-before -p "kill-pane #P? (y/n)" kill-pane # tmux >=2.2: does always use utf8, keep the option older environments # using tmux <2.2 (i.e. Debian Jessie) set -gq status-utf8 on set -g status-bg blue set -g status-fg brightgreen set -g status-justify right set -g status-left '%R ' set -g status-right '' apt-dater/conf/apt-dater.xml.in0000664000175000017500000000522415131461765014476 0ustar meme apt-dater/conf/screenrc0000664000175000017500000000361415131461765013216 0ustar meme# ------------------------------------------------------------------------------ # SCREEN SETTINGS # ------------------------------------------------------------------------------ startup_message off #defflow on # will force screen to process ^S/^Q deflogin on autodetach on # turn visual bell on vbell on # define a bigger scrollback, default is 100 lines defscrollback 2048 # ------------------------------------------------------------------------------ # SCREEN KEYBINDINGS # ------------------------------------------------------------------------------ # Remove some stupid / dangerous key bindings bind ^k #bind L bind ^\ # Make them better bind \\ quit bind K kill bind I login on bind O login off bind } history # Sessions should stay until destroyed by pressing q zombie 'q' # ------------------------------------------------------------------------------ # TERMINAL SETTINGS # ------------------------------------------------------------------------------ # The vt100 description does not mention "dl". *sigh* termcapinfo vt100 dl=5\E[M # Set the hardstatus prop on gui terms to set the titlebar/icon title termcapinfo xterm*|rxvt*|kterm*|Eterm* hs:ts=\E]0;:fs=\007:ds=\E]0;\007:OP # set these terminals up to be 'optimal' instead of vt100 #termcapinfo xterm*|linux*|rxvt*|Eterm* OP # Change the xterm initialization string from is2=\E[!p\E[?3;4l\E[4l\E> # (This fixes the "Aborted because of window size change" konsole symptoms found # in bug #134198) termcapinfo xterm 'is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;4;6l' # To get screen to add lines to xterm's scrollback buffer, uncomment the # following termcapinfo line which tells xterm to use the normal screen buffer # (which has scrollback), not the alternate screen buffer. # termcapinfo xterm|xterms|xs|rxvt ti@:te@ # Add caption line with clock, window title and window flags. caption always "%{b bG}%c%=%t%=%f" # Catch zmodem file transfers zmodem catch apt-dater/conf/hosts.xml.in0000664000175000017500000000304115131461765013750 0ustar meme apt-dater/Makefile.am0000664000175000017500000000263215131461765012575 0ustar memeSUBDIRS = conf po man src lib etc schema EXTRA_DIST = \ build \ images \ include \ test \ xmlreport \ README.autoref \ README.clusters \ README.history \ README.hooks \ README.md \ README.tagging \ README.tclfilter \ README.ttymux \ README.xmlreport AUTOMAKE_OPTIONS = 1.16 dist-bzip2 distclean-local: rm -rf *.cache *~ .project .settings .cdtproject rm -f po/*.gmo rm -f po/stamp-po ACLOCAL_AMFLAGS = -I m4 check-gettext: @if test x$(USE_NLS) != "xyes" ; then echo "Missing gettext. Rerun configure and check for" \ "'checking whether to use NLS... yes'!" ; exit 1 ; fi update-po: check-gettext @find $(srcdir)/src/ -name "*.c" -print | sort > $(srcdir)/po/POTFILES.in.2 ; \ if diff $(srcdir)/po/POTFILES.in $(srcdir)/po/POTFILES.in.2 >/dev/null 2>&1 ; then \ rm -f $(srcdir)/po/POTFILES.in.2 ; \ else \ mv $(srcdir)/po/POTFILES.in.2 $(srcdir)/po/POTFILES.in ; \ fi cd po && $(MAKE) $(AM_MAKEFLAGS) update-po update-gmo: check-gettext cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo force-update-gmo: check-gettext touch po/*.po cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo force-update-gmo-%: check-gettext @language=`echo $@ | sed s/force-update-gmo-//` ; \ if test ! -f po/$$language.po ; then echo "file po/$$language.po does not exist" ; exit 1 ; fi ; \ touch po/$$language.po ; \ cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo .PHONY: check-gettext update-po update-gmo force-update-gmo apt-dater/AUTHORS0000664000175000017500000000017515131461765011611 0ustar memeCurrent ======= Thomas Liske Alumni ====== Andre Ellguth Jens Haser apt-dater/NEWS0000664000175000017500000000064215131461765011237 0ustar memeChanges in 1.0.2 ================ The config file backend has been switched from glib's keyfile to libxml2. The filenames have changed: apt-dater.conf => apt-dater.xml hosts.conf => hosts.xml apt-dater uses a external perl script to convert the legacy hosts.conf file. Manual convertion of apt-dater.conf is required! apt-dater creates some default config files if a file does not exist during startup. apt-dater/xmlreport/0000775000175000017500000000000015131461765012572 5ustar memeapt-dater/xmlreport/overview.xsl0000664000175000017500000000371115131461765015172 0ustar meme apt-dater host overview ======================= [] Hostname : Status : LSB : Kernel : (reboot needed) Packages installed: Updates : Packages hold back: Extra packages : apt-dater/xmlreport/extra-pkgs.xsl0000664000175000017500000000272115131461765015411 0ustar meme hosts w/ extra packages installed ================================= No hosts with extra packages found! [] : () apt-dater/README.tclfilter0000664000175000017500000000541715131461765013414 0ustar memeTCL Filter ========== Since version 0.5.9, apt-dater can be configured with TCL filter support. This feature is disabled by default, enable it with configure: $ ./configure --enable-tclfilter You need a recent TCL version installed to succeed, the Debian packages have TCL filter support enabled. Usage ===== TCL filters can be used to group hosts by your own filter definition. For each host configured, apt-dater evals your TCL filter and adds the host to the category "Filtered" in the main screen if the return value is non-zero. If you have a large number of hosts, it becomes hard i.e. to find out which hosts have a certain package installed/running an obsolete release... . TCL filter helps to filter alle hosts by such conditions. Variables ========= Before evaluating a TCL filter expression for a host, apt-dater sets the following variables in the TCL environment: -------------+------------------------------------------------------- Variable | Description -------------+------------------------------------------------------- $cat | category index (0=Updates pending, 1=Up to date, ...) $group | name of the group the hosts belongs to $hostname | hostname $comment | host comment $kernel | kernel release (uname -r) $lsb_cname | "Codename"-field of lsb_release -a $lsb_rel | "Release"-field of lsb_release -a $lsb_distri | "Distributer ID"-field of lsb_release -a $extras | array of extra packages $flags | array of host flags (h x R K) $holds | array of packages hold back $installed | array of installed packages $updates | array of packages w/ update available $clusters | array of cluster names -------------+------------------------------------------------------- The filter has the full power of TCL. Examples ======== Filter all Debian hosts: return [expr [string compare $lsb_distri "Debian"] == 0] Filter all Debian hosts running a release older than Etch: return [expr [string compare $lsb_distri "Debian"] == 0 && $lsb_rel < 4.0] Filter add hosts which have bind installed: return [llength [array names installed "bind*"]] Advanced examples ================= Filter all Ubuntu 12.04 hosts wihout linux-generic-lts-trusty installed: return [[expr [string compare $lsb_rel "12.04"] == 0] && \ [expr [llength [array names installed "linux-generic-lts-trusty"]] == 0]] Filter all Ubuntu 12.04 hosts with kernel versions different from 3.2 or 3.13 and wihout linux-generic-lts-trusty installed at the same time: return [expr [expr [string compare $lsb_rel "12.04"] == 0] && \ [expr [string first "3.2." $kernel] != 0] && \ [expr [string first "3.13." $kernel] != 0] && \ [expr [llength [array names installed "linux-generic-lts-trusty"]] == 0]] apt-dater/README.autoref0000664000175000017500000000162415131461765013065 0ustar memeAuto Refresh ============ Since version 0.6.5, apt-dater has a 'auto refresh' feature. This gives two advantages: * If one host is refreshed and got some updates, one should refresh other hosts which are running the same distribution and have the same (or atleast some) packages installed, too. * There are some fundamental problems on replay attacks on todays package managers[1]. If one has several hosts (using different repository mirrors and different uplinks etc.), there is a chance that some of your hosts got recent package lists. apt-dater collects update informations over SSH on trusted hosts. If apt-dater compares those information it could detect such replay attacks. Links ===== [1] Attacks on Package Managers (Justin Cappos, Justin Samuel, Scott Baker, John H. Hartman) https://www2.cs.arizona.edu/stork/packagemanagersecurity/attacks-on-package-managers.html apt-dater/README.ttymux0000664000175000017500000000213015131461765012763 0ustar memetty muxer ========= apt-dater uses a 3rd party tty muxer. In version before apt-dater 1.0.2 only screen(1) was supported. Since apt-dater 1.0.2 tmux(1) is also supported. The tty muxer backend is selected at compile time. screen ------ For each session an induvidual screen process is launched. Sessions are not accessible by other users (limited by screen). tmux ---- For each host a shared tmux daemon is created by the first session. Sessions can be shared between users. The default configuration (~/.config/*.xml) will include global configuration from /etc/apt-dater/. You have to setup the configured global directories to be accessible by all participating users: # addgroup apt-dater # mkdir /var/cache/apt-dater # chown :apt-dater /var/cache/apt-dater # chmod 02770 /var/cache/apt-dater # mkdir /var/lib/apt-dater # chown :apt-dater /var/lib/apt-dater # chmod 02770 /var/lib/apt-dater Due to the relaxed umask config in /etc/apt-dater/apt-dater.xml and the set group ID on execution of the directories all files and tmux sockets are accessible by all users belonging to the group apt-dater. apt-dater/lib/0000775000175000017500000000000015131461765011304 5ustar memeapt-dater/lib/Makefile.am0000664000175000017500000000017015131461765013336 0ustar memedist_bin_SCRIPTS=adsh pkglibexecdir=$(pkglibdir) dist_pkglibexec_SCRIPTS=cmd hosts2xml ssh-addonce tmux-hint pcre-less apt-dater/lib/pcre-less0000775000175000017500000000174215131461765013133 0ustar meme#!/bin/sh # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2009-2016 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # grep --color=always -aisPe "$1|$" "$2" | less -R apt-dater/lib/tmux-hint0000775000175000017500000000225515131461765013173 0ustar meme#!/bin/sh # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2009-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # if [ -z "$TMUX_PANE" ]; then exit 0 fi KEY_QUIT='q' case "$AD_ACTION" in refresh) exit 0 ;; *) tmux bind-key -T root $KEY_QUIT kill-pane echo "=== Session terminated, press '$KEY_QUIT' to close pane. ===" 1>&2 exit 0 ;; esac apt-dater/lib/cmd0000775000175000017500000001167515131461765012007 0ustar meme#!/bin/sh # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2009-2014 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # run_hist_cmd () { echo -n > "$AD_HIST_PATH/command" for i in "$@"; do echo -n "\"$i\" " >> "$AD_HIST_PATH/command" done chmod +x "$AD_HIST_PATH/command" script -c "$AD_HIST_PATH/command" -t "$AD_HIST_PATH/typescript" 2> "$AD_HIST_PATH/timingfile" echo -n "Duration=" >> "$AD_HIST_PATH/meta" cat "$AD_HIST_PATH/timingfile" | cut '-d ' -f1 | awk '{ s += $1 } END { print s }' >> "$AD_HIST_PATH/meta" if (grep -aiqsPe "$AD_HIST_ERRPATTERN" "$AD_HIST_PATH/typescript"); then echo "ERRPATTERN=$AD_HIST_ERRPATTERN" >> "$AD_HIST_PATH/meta" touch "$AD_HIST_PATH/failed" fi } run_cmd () { $@ } run_hook () { if [ -n "$1" -a -d "$1" ]; then run-parts "$1" fi } if [ -x /usr/bin/lockfile-create -a -x /usr/bin/lockfile-remove ]; then LOCK_ACQ=/usr/bin/lockfile-create LOCK_REL=/usr/bin/lockfile-remove else if [ -x /usr/bin/lockfile ]; then LOCK_ACQ=/usr/bin/lockfile LOCK_REL="rm -f" fi fi lock_acq () { if [ -z "$LOCK_ACQ" ]; then echo echo "FATAL ERROR" echo "===========" echo echo "Your environment does not have lockfile(-create|-remove) available!" echo "You need to install lockfile-progs or procmail before using" echo "apt-dater-host clusters (see README.clusters)!" echo exit 1 fi $LOCK_ACQ $1 } lock_rel () { $LOCK_REL $1 } lock_cluster () { if [ "$AD_CLUSTERS" != "0" ]; then echo echo Acquiring cluster locks: REL="" for i in `seq 1 $AD_CLUSTERS`; do CLUSTER=`eval echo "\\\$AD_CLUSTER$i"` echo " $CLUSTER" LOCK_FILE="$AD_STATSDIR/_cluster_$CLUSTER" lock_acq $LOCK_FILE REL="echo ' $CLUSTER';lock_rel $LOCK_FILE;$REL" trap "echo;echo Releasing cluster locks:;$REL echo .Done.;echo" INT TERM EXIT done echo "Done." echo fi } if [ "$AD_HIST_RECORD" = "true" -a "$AD_ACTION" != 'refresh' ]; then WRAP="run_hist_cmd" else WRAP="run_cmd" fi handle_generic_ssh () { AD_CMD_REFRESH="apt-dater-host refresh" AD_CMD_UPGRADE="apt-dater-host upgrade" AD_CMD_INSTALL="apt-dater-host install %s" if [ -n "$AD_SSH_USER" ]; then my_ssh_user="-l $AD_SSH_USER"; else my_ssh_user="" fi if [ -n "$AD_SSH_PORT" ]; then my_ssh_port="-p $AD_SSH_PORT"; else my_ssh_port="" fi case "$AD_ACTION" in adsh) run_hook "$AD_HOOK_PRE_CONNECT" $WRAP $AD_SSH_CMD $@ run_hook "$AD_HOOK_POST_CONNECT" ;; connect) run_hook "$AD_HOOK_PRE_CONNECT" $WRAP $AD_SSH_CMD $AD_SSH_ID $AD_SSH_OPTFLAGS $my_ssh_user $my_ssh_port "$AD_SSH_HOST" run_hook "$AD_HOOK_POST_CONNECT" ;; transfer) if [ -n "$AD_SSH_USER" ]; then my_sftp_user="$AD_SSH_USER@"; else my_sftp_user="" fi if [ -n "$AD_SSH_PORT" ]; then my_sftp_port="-oPort=$AD_SSH_PORT"; else my_sftp_port="" fi run_hook "$AD_HOOK_PRE_CONNECT" $WRAP $AD_SFTP_CMD $AD_SSH_ID $my_sftp_port $my_sftp_user"$AD_SSH_HOST" run_hook "$AD_HOOK_POST_CONNECT" ;; install) lock_cluster run_hook "$AD_HOOK_PRE_INSTALL" $WRAP $AD_SSH_CMD $AD_SSH_ID $AD_SSH_OPTFLAGS $my_ssh_user $my_ssh_port "$AD_SSH_HOST" `printf "$AD_CMD_INSTALL" "$AD_PARAM"` run_hook "$AD_HOOK_POST_INSTALL" ;; upgrade) lock_cluster run_hook "$AD_HOOK_PRE_UPGRADE" $WRAP $AD_SSH_CMD $AD_SSH_ID $AD_SSH_OPTFLAGS $my_ssh_user $my_ssh_port "$AD_SSH_HOST" $AD_CMD_UPGRADE run_hook "$AD_HOOK_POST_UPGRADE" ;; refresh) run_hook "$AD_HOOK_PRE_REFRESH" $AD_SSH_CMD $AD_SSH_ID $AD_SSH_OPTFLAGS -n -o BatchMode=yes -o ConnectTimeout=5 $my_ssh_user $my_ssh_port "$AD_SSH_HOST" $AD_CMD_REFRESH 2>&1 run_hook "$AD_HOOK_POST_REFRESH" ;; *) echo "Unhandled action '$AD_ACTION'!" 1>&2 echo 1>&2 set | grep ^AD_ 1>&2 echo 1>&2 exit 1; ;; esac } if [ -z "$AD_TYPE" -o "$AD_TYPE" = 'generic-ssh' ]; then handle_generic_ssh $@ else if [ -x "$AD_PLUGINDIR/$AD_TYPE/cmd" ]; then $WRAP "$AD_PLUGINDIR/$AD_TYPE/cmd" $@ else echo "No plugin to handle type '$AD_TYPE'!" 1>&2 echo 1>&2 set | grep ^AD_ 1>&2 echo 1>&2 exit 2; fi fi apt-dater/lib/adsh0000775000175000017500000000224015131461765012147 0ustar meme#!/bin/sh # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # argv=$@ sport=0 while getopts :-1246AaCfgKkMNnqsTtVvXxYyb:c:D:E:e:F:I:i:L:l:m:O:o:p:Q:R:S:W:w: OPT; do case $OPT in -p) sport=$OPTARG ;; esac done shift $((OPTIND-1)) shost="$1" exec apt-dater -s "$shost:$sport" $argv apt-dater/lib/hosts2xml.in0000775000175000017500000000765015131461765013612 0ustar meme#!/usr/bin/perl use warnings; use strict; use Getopt::Std; use Glib; use XML::Writer; $Getopt::Std::STANDARD_HELP_VERSION++; my $LOGPREF = '[hosts2xml]'; sub HELP_MESSAGE { print <] [-o ] -i input filename -o output filename --help show this help --version show version information USG } sub VERSION_MESSAGE { print < Copyright Holder: 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LIC #/ } our $opt_i = glob(q(~/.config/apt-dater/hosts.conf)); our $opt_o = glob(q(~/.config/apt-dater/hosts.xml)); unless(getopts('i:o:')) { HELP_MESSAGE; exit 1; } my $fhin = IO::File->new("< $opt_i"); die "$LOGPREF Failed to open '$opt_i': $!\n" unless($fhin); my %xparams = ( DATA_MODE => 1, DATA_INDENT => 4, ); if($opt_o) { $xparams{OUTPUT} = IO::File->new(">$opt_o"); die "$LOGPREF Failed to open '$opt_o': $!\n" unless($xparams{OUTPUT}); } my $xml = XML::Writer->new(%xparams); $xml->doctype('hosts', undef, '@XMLSCHEMAURI@/hosts.dtd'); $xml->comment(" Hosts file of apt-dater (parsed by libxml2) =========================================== hosts.xml configures the hosts which are managed by apt-dater. Host options (except 'name') are lookuped as attributes at the host node itself, the parent group node and the global /hosts/default node. The following attributes are known: - name : visible name of the host or group (required) - comment : text shown in 'host details' screen - type : transport type (default: 'generic-ssh') - ssh-user: overwrite SSH username - ssh-host: overwrite SSH host (defaults to \@name) - ssh-port: overwrite SSH port - ssh-id : overwrite SSH identification file Example: ... "); $xml->startTag('hosts', 'xmlns:xi' => q(http://www.w3.org/2001/XInclude)); $xml->comment('Include global config file if available.'); $xml->startTag('xi:include', href => q(file:///etc/apt-dater/hosts.xml), xpointer => q(xpointer(/hosts/*))); $xml->emptyTag('xi:fallback'); $xml->endTag('xi:include'); my %sections; my @sections; my $csect; while(<$fhin>) { chomp; s/(^\s+|\s+$|#.*$)//g; next unless($_); if(/^\[([^\]]+)\]/) { $csect = $1; push(@sections, $csect); next; } if(/^(Hosts)=(.+)/i) { $sections{$csect}->{lc($1)} = $2; next; } warn("$LOGPREF Garbage line: $_\n"); } $fhin->close; foreach my $csect (sort @sections) { my @group = ('group', name => $csect); if($sections{$csect}->{type}) { push(@group, type => $sections{$csect}->{type}); } $xml->startTag(@group); foreach my $host (sort split(/;/, $sections{$csect}->{hosts})) { my $sport; $sport = $1 if($host =~ s/:(\d+)//); my $suser; $suser = $1 if($host =~ s/(.+)@//); my @host = ('host', name => $host); push(@host, 'ssh-user' => $suser) if($suser); push(@host, 'ssh-port' => $sport) if($sport); $xml->emptyTag(@host); } $xml->endTag('group'); } $xml->endTag('hosts'); $xml->end(); $xparams{OUTPUT}->close() if($xparams{OUTPUT}); print STDERR "$LOGPREF '$opt_i' has been converted to '$opt_o'!\n"; apt-dater/lib/ssh-addonce0000775000175000017500000000251515131461765013425 0ustar meme#!/bin/sh # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2009-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # if [ "$#" = "0" ]; then echo "Usage: $0 ..." 1>&2 exit 1 fi for KEYFN in $@; do if [ ! -r "$KEYFN" ]; then echo "Identity '$KEYFN' is not readable!" 1>&2 else ssh-keygen -l -f "$KEYFN" | cut '-d ' -f1,2 | while read keyid; do o=$(ssh-add -l | grep "^$keyid ") if [ -z "$o" ]; then exec ssh-add "$KEYFN" else echo "Identity found: $KEYFN" fi done fi done apt-dater/test/0000775000175000017500000000000015131461765011515 5ustar memeapt-dater/test/logtest-falsepositives.txt0000664000175000017500000000025215131461765016774 0ustar memedkms: WARNING: Linux headers are missing, which may explain the above failures. insserv: warning: script 'K01ppp' missing LSB tags and overrides Restart fail2ban service apt-dater/test/logtest-notices.txt0000664000175000017500000000042515131461765015402 0ustar meme/usr/sbin/grub-probe: warning: Couldn't find physical volume `pv1'. Some modules may be missing from core image.. Job for supervisor.service failed. See 'systemctl status supervisor.service' and 'journalctl -xn' for details. Some fatal error needs your attention Help I failed apt-dater/test/test.sh0000775000175000017500000000252415131461765013036 0ustar meme#!/bin/bash # # Minimalistic test suite for the regular expression 'err-pattern' # #set -x # Default pattern from src/keyfiles.c PATTERN=$(grep err-pattern ../src/keyfiles.c | cut -d\" -f4) # override with a TEST patten PATTERN="((? # # Copyright Holder: # 2010-2014 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Tries to get the path where screen stores it's sockets. screen=${1:-/usr/bin/screen} if [ -x "$screen" ]; then LANG=C $screen -list | perl -ne 'print "$2\n" if(/^\S+ Sockets? (found )?in (\/.+)\/[^\/]+\.$/);' fi apt-dater/build/travis-install.sh0000775000175000017500000000017015131461765015146 0ustar meme#!/bin/sh -xe cd $(dirname $0)/.. aclocal autoconf automake --add-missing --force-missing ./configure $features make apt-dater/build/travis-before.sh0000775000175000017500000000032015131461765014737 0ustar meme#!/bin/sh -xe cd $(dirname $0)/.. apt-get update apt-get dist-upgrade -y apt-get install -y automake gettext libpopt-dev libglib2.0-dev libncursesw5-dev tcl8.6-dev libxml2-dev libconfig-dev screen tmux xxd apt-dater/build/apt-dater.spec.in0000664000175000017500000000536215131461765015005 0ustar meme # apt-dater - terminal-based remote package update manager # # Authors: # Thomas Liske # # Copyright Holder: # 2010-2014 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] # # License: # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # %define name @PACKAGE@ %define version @PACKAGE_VERSION@ %define release 1 Name: %{name} Summary: Terminal-based remote package update manager Version: %{version} Release: %{release} URL: https://www.ibh.de/apt-dater/ Source: %{name}-%{version}.tar.gz License: GPL Group: System/Management Icon: apt-dater.xpm Requires: screen Requires: tcl BuildRequires: libconfig-devel BuildRequires: gettext-devel BuildRequires: glib2-devel BuildRequires: libxml2-devel BuildRequires: ncurses-devel BuildRequires: popt-devel BuildRequires: screen BuildRequires: tcl-devel BuildRequires: automake BuildRequires: autoconf BuildRequires: perl Buildroot: %{_tmppath}/%{name}-buildroot Vendor: IBH IT-Service GmbH (https://www.ibh.de/) %description apt-dater provides an easy to use ncurses frontend for managing package updates on a large number of remote hosts using SSH. It supports Debian-based managed hosts as well as OpenSUSE and CentOS based systems. %prep %setup %build %configure --prefix=%{_prefix} --libexec=%{_libexecdir}/apt-dater --disable-rpath --enable-tclfilter --enable-xmlreport --enable-autoref --enable-history --enable-clusters --enable-debug make %install (cd src && make install DESTDIR=$RPM_BUILD_ROOT) (cd lib && make install DESTDIR=$RPM_BUILD_ROOT) (cd po && make install DESTDIR=$RPM_BUILD_ROOT) (cd man && make install DESTDIR=$RPM_BUILD_ROOT) rm -f $RPM_BUILD_ROOT/usr/share/man/man1/apt-dater-host.1 %clean rm -rf $RPM_BUILD_ROOT make clean %files %defattr(-,root,root) %{_bindir}/apt-dater %dir %{_libdir}/apt-dater %{_libdir}/apt-dater/* %doc AUTHORS COPYING ChangeLog README* TODO %{_mandir}/man5/*.5* %{_mandir}/man8/*.8* %dir %{_mandir}/manh %{_mandir}/manh/* %lang(de) %{_datadir}/locale/de/LC_MESSAGES/apt-dater.mo %lang(it) %{_datadir}/locale/it/LC_MESSAGES/apt-dater.mo %lang(pt) %{_datadir}/locale/pt/LC_MESSAGES/apt-dater.mo apt-dater/po/0000775000175000017500000000000015131461765011154 5ustar memeapt-dater/po/it.po0000664000175000017500000003607315131461765012141 0ustar meme# Italian translations for apt-dater package # Copyright (C) 2009 THE apt-dater'S COPYRIGHT HOLDER # This file is distributed under the same license as the apt-dater package. # Milo Casagrande , 2009. # msgid "" msgstr "" "Project-Id-Version: apt-dater 0.8.0\n" "Report-Msgid-Bugs-To: apt-dater@ibh.de\n" "POT-Creation-Date: 2014-10-28 18:26+0100\n" "PO-Revision-Date: 2009-11-11 22:34+0100\n" "Last-Translator: Milo Casagrande \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: src/apt-dater.c:79 src/apt-dater.c:82 src/apt-dater.c:121 src/keyfiles.c:46 #: src/keyfiles.c:60 src/ui.c:3496 msgid "Out of memory." msgstr "Memoria esaurita." #: src/apt-dater.c:88 #, c-format msgid "Failed to create initial configuration file %s." msgstr "Creazione del file di configurazione iniziale %s non riuscita." #: src/apt-dater.c:104 msgid "Sorry, apt-dater was compiled w/o report feature!" msgstr "apt-dater è stato compilato senza la funzionalità di rapporto." #: src/apt-dater.c:114 #, fuzzy, c-format msgid "Usage: %s [-(c config|v|[n]r)]\n" msgstr "Uso: %s [-(c config|v|r)]\n" #: src/apt-dater.c:116 #, fuzzy, c-format msgid "Usage: %s [-(c config|v)]\n" msgstr "Uso: %s [-(c config|v|r)]\n" #: src/apt-dater.c:126 src/apt-dater.c:131 #, c-format msgid "Error on loading config file %s\n" msgstr "Errore nel caricare il file di configurazione %s\n" #: src/apt-dater.c:184 msgid "Cannot open your terminal /proc/self/fd/0 - please check." msgstr "Impossibile aprire il terminale /proc/self/fd/0 - controllare." #: src/colors.c:150 msgid "Wrong color definition!" msgstr "Definizione colore errata." #: src/keyfiles.c:50 #, fuzzy, c-format msgid "Creating default config file %s" msgstr "Creazione del file %s" #: src/keyfiles.c:52 #, c-format msgid "Could not write to file %s." msgstr "" #: src/keyfiles.c:62 #, c-format msgid "Mandatory config file %s does not exist!" msgstr "" #: src/lock.c:53 msgid "Can't get the name of the lock file!" msgstr "Impossibile ottenere il nome del file di blocco." #: src/lock.c:74 #, c-format msgid "Failed to get lockfile %s: %s" msgstr "Impossibile ottenere il file di blocco %s: %s" #: src/lock.c:78 #, c-format msgid "Can't lock to file %s because function flock() is missing!" msgstr "Impossibile bloccare il file %s perché manca la funzione flock()." #: src/report.c:55 msgid "Error creating the xml output." msgstr "Errore nel creare l'ouput XML." #: src/report.c:60 #, c-format msgid "apt-dater is refreshing %d hosts, please standby...\n" msgstr "apt-dater sta aggiornando %d host, attendere...\n" #: src/stats.c:541 msgid "Auto refresh triggered..." msgstr "Aggiornamento automatico avviato..." #: src/ui.c:52 msgid "Updates pending" msgstr "Aggiornamenti in attesa" #: src/ui.c:53 msgid "Up to date" msgstr "Aggiornato" #: src/ui.c:54 msgid "Broken packages" msgstr "Pacchetti danneggiati" #: src/ui.c:55 src/ui.c:1417 #, c-format msgid "Refresh required" msgstr "Aggiornamento richiesto" #: src/ui.c:56 src/ui.c:1420 #, c-format msgid "In refresh" msgstr "Aggiornamento in corso" #: src/ui.c:57 msgid "Sessions" msgstr "Sessioni" #: src/ui.c:59 msgid "Filtered" msgstr "Filtrato" #: src/ui.c:61 msgid "Unknown" msgstr "Sconosciuto" #: src/ui.c:154 msgid "" msgstr "" #: src/ui.c:154 src/ui.c:155 msgid "shrink node" msgstr "riduce il nodo" #: src/ui.c:156 msgid "" msgstr "" #: src/ui.c:156 src/ui.c:157 msgid "expand node" msgstr "espande il nodo" #: src/ui.c:158 msgid "" msgstr "" #: src/ui.c:158 src/ui.c:159 src/ui.c:160 src/ui.c:169 msgid "shrink/expand node" msgstr "riduce/espande il nodo" #: src/ui.c:159 msgid "" msgstr "" #: src/ui.c:160 msgid "" msgstr "" #: src/ui.c:161 msgid "" msgstr "" #: src/ui.c:161 src/ui.c:162 msgid "move up" msgstr "muove in su" #: src/ui.c:163 msgid "" msgstr "" #: src/ui.c:163 src/ui.c:164 msgid "move down" msgstr "muove in giù" #: src/ui.c:165 msgid "" msgstr "" #: src/ui.c:165 msgid "move to the top" msgstr "muove in alto" #: src/ui.c:166 msgid "" msgstr "" #: src/ui.c:166 msgid "move to the end" msgstr "muove in basso" #: src/ui.c:167 msgid "" msgstr "" #: src/ui.c:167 msgid "previous page" msgstr "pagina precedente" #: src/ui.c:168 msgid "" msgstr "" #: src/ui.c:168 msgid "next page" msgstr "pagina successiva" #: src/ui.c:170 msgid "quit" msgstr "chiude" #: src/ui.c:171 msgid "help" msgstr "aiuto" #: src/ui.c:172 msgid "search host" msgstr "cerca host" #: src/ui.c:174 msgid "filter hosts" msgstr "filtra gli host" #: src/ui.c:176 msgid "attach session" msgstr "collega sessione" #: src/ui.c:177 msgid "connect host" msgstr "connetti host" #: src/ui.c:178 msgid "file transfer" msgstr "trasferimento file" #: src/ui.c:179 msgid "toggle dumps" msgstr "commuta i dump" #: src/ui.c:180 msgid "refresh host" msgstr "aggiorna host" #: src/ui.c:181 msgid "failure diagnostic" msgstr "" #: src/ui.c:182 msgid "install pkg" msgstr "installa pkg" #: src/ui.c:183 msgid "upgrade host(s)" msgstr "aggiorna host" #: src/ui.c:184 msgid "host details" msgstr "dettagli host" #: src/ui.c:186 msgid "host history" msgstr "cronologia host" #: src/ui.c:187 msgid "play" msgstr "riproduce" #: src/ui.c:188 msgid "display with less" msgstr "visualizza con less" #: src/ui.c:190 msgid "next detached session" msgstr "sessione scollegata successiva" #: src/ui.c:191 msgid "cycle detached sessions" msgstr "cicla tra le sessione scollegate" #: src/ui.c:192 msgid "tag current host" msgstr "etichetta l'host attuale" #: src/ui.c:193 msgid "tag all hosts matching" msgstr "etichetta tutti gli host corrispondenti" #: src/ui.c:194 msgid " ~c tag by codename" msgstr "" #: src/ui.c:195 msgid " ~d tag by distribution" msgstr "" #: src/ui.c:196 msgid " ~f tag by host flags" msgstr "" #: src/ui.c:197 msgid " ~g tag by group" msgstr "" #: src/ui.c:198 msgid " ~p tag by packages" msgstr "" #: src/ui.c:199 msgid " ~u tag by updates" msgstr "" #: src/ui.c:200 #, fuzzy msgid " ~A tag all hosts" msgstr "etichetta tutti gli host corrispondenti" #: src/ui.c:201 msgid "untag all hosts matching" msgstr "toglie l'etichetta da tutti gli host corrispondenti" #: src/ui.c:202 msgid "apply next function to tagged hosts" msgstr "applica la funzione successiva agli host etichettati" #: src/ui.c:207 msgid "some packages are kept back" msgstr "alcuni pacchetti sono bloccati" #: src/ui.c:208 msgid "extra packages are installed" msgstr "pacchetti aggiuntivi sono installati" #: src/ui.c:209 msgid "pending kernel upgrade (ABI compatible)" msgstr "" #: src/ui.c:210 msgid "pending kernel upgrade" msgstr "" #: src/ui.c:211 msgid "unknown kernel upgrade state" msgstr "" #: src/ui.c:212 msgid "this is a virtualized machine" msgstr "questa è una macchina virtualizzata" #: src/ui.c:214 msgid "this machine is part of a cluster" msgstr "" #: src/ui.c:589 msgid "FLAG" msgstr "FLAG" #: src/ui.c:590 src/ui.c:603 msgid "DESCRIPTION" msgstr "DESCRIZIONE" #: src/ui.c:602 msgid "KEY" msgstr "CHIAVE" #: src/ui.c:651 msgid "HOST DETAILS" msgstr "DETTAGLI HOST" #: src/ui.c:654 msgid "Group:" msgstr "Gruppo:" #: src/ui.c:656 msgid "Hostname:" msgstr "Nome host:" #: src/ui.c:659 msgid "Comment:" msgstr "" #: src/ui.c:663 msgid "Machine Type:" msgstr "Tipo computer:" #: src/ui.c:667 msgid "Architecture:" msgstr "Architettura:" #: src/ui.c:671 msgid "UUID:" msgstr "UUID:" #: src/ui.c:675 msgid "Forbidden:" msgstr "Proibito:" #: src/ui.c:679 #, fuzzy msgid "refresh" msgstr "Aggiornamento in corso" #: src/ui.c:686 #, fuzzy msgid "upgrade" msgstr "aggiorna host" #: src/ui.c:693 #, fuzzy msgid "install" msgstr "installa pkg" #: src/ui.c:701 msgid "Distri:" msgstr "Distri:" #: src/ui.c:707 msgid "Release:" msgstr "Rilascio:" #: src/ui.c:711 msgid "Kernel name:" msgstr "Nome kernel:" #: src/ui.c:715 msgid "Kernel version:" msgstr "Versione kernel:" #: src/ui.c:720 msgid "(pending ABI compatible upgrade)" msgstr "" #: src/ui.c:723 msgid "(pending upgrade)" msgstr "" #: src/ui.c:738 msgid "Clusters: " msgstr "" #: src/ui.c:750 msgid "Packages: " msgstr "Pacchetti:" #: src/ui.c:761 msgid "BROKEN PACKAGES" msgstr "PACCHETTI DANNEGGIATI" #: src/ui.c:782 msgid "UPDATE PACKAGES" msgstr "PACCHETTI AGGIORNATI" #: src/ui.c:803 msgid "HOLD BACK PACKAGES" msgstr "PACCHETTI BLOCCATI" #: src/ui.c:823 msgid "EXTRA PACKAGES" msgstr "PACCHETTI EXTRA" #: src/ui.c:843 msgid "INSTALLED PACKAGES" msgstr "PACCHETTI INSTALLATI" #: src/ui.c:909 msgid "FAILURE DIAGNOSTIC" msgstr "" #: src/ui.c:949 #, fuzzy, c-format msgid " [Oldest: %x %X]" msgstr " [Più vecchio: %D %H.%M]" #: src/ui.c:1035 msgid "c" msgstr "c" #: src/ui.c:1035 msgid "C" msgstr "C" #: src/ui.c:1038 msgid "y" msgstr "y" #: src/ui.c:1038 msgid "Y" msgstr "Y" #: src/ui.c:1113 msgid "No history data available!" msgstr "Nessun dato di cronologia." #: src/ui.c:1119 #, c-format msgid "History of %s (%d entry available)" msgstr "Cronologia di %s (%d voce disponibile)" #: src/ui.c:1120 #, c-format msgid "History of %s (%d entries available)" msgstr "Cronologia di %s (%d voci disponibili)" #: src/ui.c:1202 msgid "replay terminated, press any key to continue" msgstr "riproduzione terminata, premere un tasto per continuare" #: src/ui.c:1261 src/ui.c:1292 #, c-format msgid "%d Hosts in status \"%s\"" msgstr "%d host nello stato \"%s\"" #: src/ui.c:1264 src/ui.c:1295 #, c-format msgid "%d Host in status \"%s\"" msgstr "%d host nello stato \"%s\"" #: src/ui.c:1396 #, c-format msgid "%d Updates required" msgstr "%d aggiornamenti richiesti" #: src/ui.c:1399 #, c-format msgid "%d Update required" msgstr "%d aggiornamento richiesto" #: src/ui.c:1404 #, c-format msgid "No update required" msgstr "Aggiornamento non richiesto" #: src/ui.c:1409 #, c-format msgid "%d Broken packages" msgstr "%d pacchetti danneggiati" #: src/ui.c:1412 #, c-format msgid "%d Broken package" msgstr "%d pacchetto danneggiato" #: src/ui.c:1425 #, c-format msgid "%d sessions running" msgstr "%d sessioni in esecuzione" #: src/ui.c:1428 #, c-format msgid "%d session running" msgstr "%d sessione in esecuzione" #: src/ui.c:1434 #, c-format msgid "Status is unknown" msgstr "Lo stato è sconosciuto" #: src/ui.c:1437 #, c-format msgid "Error: %s" msgstr "" #: src/ui.c:1443 msgid " - host locked by another process" msgstr " - host bloccato da un altro processo" #: src/ui.c:1499 #, fuzzy, c-format msgid "Running session %s [%5d]:" msgstr "Esecuzione sessione %s [%5d]:" #: src/ui.c:1525 src/ui.c:1598 msgid "%D %H:%M " msgstr "%D %H.%M " #: src/ui.c:1528 src/ui.c:1601 msgid "Attached" msgstr "Collegato" #: src/ui.c:1528 src/ui.c:1601 msgid "Detached" msgstr "Scollegato" #: src/ui.c:1540 msgid "Could not read session dump." msgstr "Impossibile leggere il dump della sessione." #: src/ui.c:1543 #, fuzzy msgid "attached" msgstr "Collegato" #: src/ui.c:1543 #, fuzzy msgid "detached" msgstr "Scollegato" #: src/ui.c:1872 msgid "Maintainer name:" msgstr "Nome maintainer:" #: src/ui.c:2493 msgid "Search: " msgstr "Cerca:" #: src/ui.c:2666 msgid "Matches:" msgstr "Corrispondenze:" #: src/ui.c:2683 msgid "Matches: -" msgstr "Corrispondenze:-" #: src/ui.c:2751 #, fuzzy msgid "Internal error: unhandled TCL TCLM_STRING mapping!" msgstr "Errore interno: mappatura TCL TCLM_STRING non gestita." #: src/ui.c:2766 #, fuzzy msgid "Internal error: unhandled TCL TCLM_INT mapping!" msgstr "Errore interno: mappatura TCL TCLM_INT." #: src/ui.c:2776 #, fuzzy msgid "Internal error: unknown TCL mapping type!" msgstr "Errore interno: tipologia di mappatura TCL sconosciuta." #: src/ui.c:2844 msgid "Scalars:" msgstr "Scalari:" #: src/ui.c:2866 msgid "Arrays:" msgstr "Array:" #: src/ui.c:2887 msgid "Examples:" msgstr "Esempi:" #: src/ui.c:2900 msgid "Enter filter expression:" msgstr "Inserire espressione di filtro:" #: src/ui.c:2934 #, fuzzy, c-format msgid "An error at %s:%d has been detected [Less/ignore/connect]: " msgstr "È stato rilevato un errore presso %s:%d [Lic]: " #: src/ui.c:2936 #, fuzzy, c-format msgid "An error at %s has been detected [Less/ignore/connect]: " msgstr "È stato rilevato un errore presso %s [Lic]: " #: src/ui.c:3113 #, c-format msgid "Refresh %d tagged hosts? [y/N]: " msgstr "Aggiornare %d host etichettati? [y/N]: " #: src/ui.c:3148 msgid "tag-" msgstr "tag-" #: src/ui.c:3162 src/ui.c:3188 src/ui.c:3316 msgid "There are running sessions on this host! Continue? [y/N]: " msgstr "" "Su questo host sono presenti sessioni in esecuzione. Continuare? [y/N]:" #: src/ui.c:3211 msgid "Run update for the whole category? [y/N]: " msgstr "Eseguire l'aggiornamento per l'intera categoria? [y/N]:" #: src/ui.c:3212 msgid "Run update for the whole group? [y/N]: " msgstr "Eseguire l'aggiornamento per l'intero gruppo? [y/N]:" #: src/ui.c:3256 #, c-format msgid "Run update for %d tagged and updatable hosts? [y/N]: " msgstr "" "Eseguire l'aggiornamento per gli host etichettati e aggiornabili (%d)? [y/N]:" #: src/ui.c:3286 #, c-format msgid "Install package `%s' [y/N]: " msgstr "Installare il pacchetto \"%s\" [y/N]:" #: src/ui.c:3292 src/ui.c:3321 src/ui.c:3337 msgid "Install package: " msgstr "Installare il pacchetto:" #: src/ui.c:3341 msgid "Run install for the whole category? [y/N]: " msgstr "Eseguire l'installazione per l'intera categoria? [y/N]:" #: src/ui.c:3342 msgid "Run install for the whole group? [y/N]: " msgstr "Eseguire l'installazione per l'intero gruppo? [y/N]:" #: src/ui.c:3387 #, fuzzy, c-format msgid "Install package on %d tagged hosts: " msgstr "Installare il pacchetto su %d host etichettati:" #: src/ui.c:3415 msgid "Tag hosts matching: " msgstr "Etichettare host corrispondenti:" #: src/ui.c:3416 msgid "Untag hosts matching: " msgstr "Togliere etichetta a host corrispondenti:" #: src/ui.c:3491 #, fuzzy, c-format msgid "Attach host %s session %d (%d %s left) [Y/n/c]: " msgstr "Collegare host %s sessione %d (%d %s mancanti) [Y/n/c]:" #: src/ui.c:3493 msgid "session" msgstr "sessione" #: src/ui.c:3493 msgid "sessions" msgstr "sessioni" #: src/ui.c:3565 msgid "Already attached - share session? [y/N]: " msgstr "Già collegato, condividere la sessione? [y/N]:" #: src/ui.c:3593 msgid "Session dumps enabled." msgstr "Dump della sessione abilitati." #: src/ui.c:3595 msgid "Session dumps disabled." msgstr "Dump della sessione disabilitati." #: src/ui.c:3693 #, c-format msgid "There are %d hosts in status refresh state, quit apt-dater? [y/N]: " msgstr "" #: src/ui.c:3696 #, c-format msgid "There is %d host in status refresh state, quit apt-dater? [y/N]: " msgstr "" #~ msgid "running kernel is not the latest (reboot required)" #~ msgstr "il kernel in esecuzione non è il più rencente (riavvio richiesto)" #~ msgid "a selfbuild kernel is running" #~ msgstr "è in esecuzione un kernel generato manualmente" #~ msgid "(reboot required)" #~ msgstr "(riavvio richiesto)" #~ msgid "(selfbuild kernel)" #~ msgstr "(kernel generato manualmente)" #, fuzzy #~ msgid "install pkg on tagged host(s)" #~ msgstr "Installare il pacchetto su %d host etichettati:" #, fuzzy #~ msgid "upgrade tagged host(s)" #~ msgstr "aggiorna host" apt-dater/po/POTFILES.in0000664000175000017500000000040315131461765012726 0ustar meme./src/apt-dater.c ./src/autoref.c ./src/clusters.c ./src/colors.c ./src/completion.c ./src/env.c ./src/exec.c ./src/history.c ./src/keyfiles.c ./src/lock.c ./src/parsecmd.c ./src/report.c ./src/screen.c ./src/sighandler.c ./src/stats.c ./src/tag.c ./src/ui.c apt-dater/po/de.po0000664000175000017500000003510415131461765012107 0ustar meme# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR IBH IT-Service GmbH # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: apt-dater 1.0.0\n" "Report-Msgid-Bugs-To: apt-dater@ibh.de\n" "POT-Creation-Date: 2014-10-28 18:26+0100\n" "PO-Revision-Date: 2014-10-28 18:30+0100\n" "Last-Translator: Thomas Liske \n" "Language-Team: apt-dater@ibh.de\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/apt-dater.c:79 src/apt-dater.c:82 src/apt-dater.c:121 src/keyfiles.c:46 #: src/keyfiles.c:60 src/ui.c:3496 msgid "Out of memory." msgstr "Nicht genügend Speicher." #: src/apt-dater.c:88 #, c-format msgid "Failed to create initial configuration file %s." msgstr "Die initiale Konfigurationsdatei %s konnte nicht erstellt werden." #: src/apt-dater.c:104 msgid "Sorry, apt-dater was compiled w/o report feature!" msgstr "Leider wurde apt-dater ohne Report-Merkmal übersetzt." #: src/apt-dater.c:114 #, c-format msgid "Usage: %s [-(c config|v|[n]r)]\n" msgstr "Aufruf: %s [-(c config|v|[n]r)]\n" #: src/apt-dater.c:116 #, c-format msgid "Usage: %s [-(c config|v)]\n" msgstr "Aufruf: %s [-(c config|v)]\n" #: src/apt-dater.c:126 src/apt-dater.c:131 #, c-format msgid "Error on loading config file %s\n" msgstr "Fehler beim Laden der Konfigurationsdatei %s\n" #: src/apt-dater.c:184 msgid "Cannot open your terminal /proc/self/fd/0 - please check." msgstr "Kann Terminal /proc/self/fd/0 nicht öffnen - bitte überprüfen." #: src/colors.c:150 msgid "Wrong color definition!" msgstr "Fehlerhafte Farbdefinition!" #: src/keyfiles.c:50 #, fuzzy, c-format msgid "Creating default config file %s" msgstr "Erstelle Datei %s" #: src/keyfiles.c:52 #, c-format msgid "Could not write to file %s." msgstr "Kann nicht in Datei %s schreiben." #: src/keyfiles.c:62 #, c-format msgid "Mandatory config file %s does not exist!" msgstr "Die benötigte Konfigurationsdatei %s existiert nicht!" #: src/lock.c:53 msgid "Can't get the name of the lock file!" msgstr "Kann Namen der Lock-Datei nicht ermitteln!" #: src/lock.c:74 #, c-format msgid "Failed to get lockfile %s: %s" msgstr "Fehler beim Anlegen der Lock-Datei %s: %s" #: src/lock.c:78 #, c-format msgid "Can't lock to file %s because function flock() is missing!" msgstr "Kann Datei %s nicht sperren da flock() nicht zur Verfügung steht!" #: src/report.c:55 msgid "Error creating the xml output." msgstr "Fehler beim Erstellen der XML-Ausgabe." #: src/report.c:60 #, c-format msgid "apt-dater is refreshing %d hosts, please standby...\n" msgstr "apt-dater aktualisiert %d Hosts, bitte warten...\n" #: src/stats.c:541 msgid "Auto refresh triggered..." msgstr "Aktualisierung ausgelöst..." #: src/ui.c:52 msgid "Updates pending" msgstr "Updates vorhanden" #: src/ui.c:53 msgid "Up to date" msgstr "Aktuell" #: src/ui.c:54 msgid "Broken packages" msgstr "Defekte Pakete" #: src/ui.c:55 src/ui.c:1417 #, c-format msgid "Refresh required" msgstr "Aktualisierung benötigt" #: src/ui.c:56 src/ui.c:1420 #, c-format msgid "In refresh" msgstr "Wird aktualisiert" #: src/ui.c:57 msgid "Sessions" msgstr "Sitzungen" #: src/ui.c:59 msgid "Filtered" msgstr "Gefiltert" #: src/ui.c:61 msgid "Unknown" msgstr "Unbekannt" #: src/ui.c:154 msgid "" msgstr "" #: src/ui.c:154 src/ui.c:155 msgid "shrink node" msgstr "Baum zusammenklappen" #: src/ui.c:156 msgid "" msgstr "" #: src/ui.c:156 src/ui.c:157 msgid "expand node" msgstr "Baum aufklappen" #: src/ui.c:158 msgid "" msgstr "" #: src/ui.c:158 src/ui.c:159 src/ui.c:160 src/ui.c:169 msgid "shrink/expand node" msgstr "Baum auf-/zusammenklappen" #: src/ui.c:159 msgid "" msgstr "" #: src/ui.c:160 msgid "" msgstr "" #: src/ui.c:161 msgid "" msgstr "" #: src/ui.c:161 src/ui.c:162 msgid "move up" msgstr "nach oben" #: src/ui.c:163 msgid "" msgstr "" #: src/ui.c:163 src/ui.c:164 msgid "move down" msgstr "nach unten" #: src/ui.c:165 msgid "" msgstr "" #: src/ui.c:165 msgid "move to the top" msgstr "an den Anfang" #: src/ui.c:166 msgid "" msgstr "" #: src/ui.c:166 msgid "move to the end" msgstr "an das Ende" #: src/ui.c:167 msgid "" msgstr "Bild hoch" #: src/ui.c:167 msgid "previous page" msgstr "vorherige Seite" #: src/ui.c:168 msgid "" msgstr "Bild runter" #: src/ui.c:168 msgid "next page" msgstr "nächste Seite" #: src/ui.c:170 msgid "quit" msgstr "Beenden" #: src/ui.c:171 msgid "help" msgstr "Hilfe" #: src/ui.c:172 msgid "search host" msgstr "Host suchen" #: src/ui.c:174 msgid "filter hosts" msgstr "Hosts filtern" #: src/ui.c:176 msgid "attach session" msgstr "mit Sitzung verbinden" #: src/ui.c:177 msgid "connect host" msgstr "Host verbinden" #: src/ui.c:178 msgid "file transfer" msgstr "Dateiübertragung" #: src/ui.c:179 msgid "toggle dumps" msgstr "Ausgabe an/aus" #: src/ui.c:180 msgid "refresh host" msgstr "Host aktualisieren" #: src/ui.c:181 msgid "failure diagnostic" msgstr "Fehler Diagnose" #: src/ui.c:182 msgid "install pkg" msgstr "Paket installieren" #: src/ui.c:183 msgid "upgrade host(s)" msgstr "Host(s) upgraden" #: src/ui.c:184 msgid "host details" msgstr "Host Details" #: src/ui.c:186 msgid "host history" msgstr "Host Historie" #: src/ui.c:187 msgid "play" msgstr "wiedergeben" #: src/ui.c:188 msgid "display with less" msgstr "mit less anzeigen" #: src/ui.c:190 msgid "next detached session" msgstr "nächste ungenutzt Sitzung" #: src/ui.c:191 msgid "cycle detached sessions" msgstr "ungenutzt Sitzungen durchgehen" #: src/ui.c:192 msgid "tag current host" msgstr "Host markieren" #: src/ui.c:193 msgid "tag all hosts matching" msgstr "Hosts suchen und markieren" #: src/ui.c:194 msgid " ~c tag by codename" msgstr " ~c markieren nach Codename" #: src/ui.c:195 msgid " ~d tag by distribution" msgstr " ~d markieren nach Distribution" #: src/ui.c:196 msgid " ~f tag by host flags" msgstr " ~f markieren nach Host Flags" #: src/ui.c:197 msgid " ~g tag by group" msgstr " ~g markieren nach Gruppe" #: src/ui.c:198 msgid " ~p tag by packages" msgstr " ~p markieren nach Paketen" #: src/ui.c:199 msgid " ~u tag by updates" msgstr " ~u markieren nach Updates" #: src/ui.c:200 msgid " ~A tag all hosts" msgstr " ~A alle Hosts markieren" #: src/ui.c:201 msgid "untag all hosts matching" msgstr "Hosts suchen und demarkieren" #: src/ui.c:202 msgid "apply next function to tagged hosts" msgstr "folgende Funktion auf markierte Hosts anwenden" #: src/ui.c:207 msgid "some packages are kept back" msgstr "Pakete wurden zurückgehalten" #: src/ui.c:208 msgid "extra packages are installed" msgstr "extra Pakete installiert" #: src/ui.c:209 msgid "pending kernel upgrade (ABI compatible)" msgstr "Kernel Upgrade vorhanden (ABI kompatibel)" #: src/ui.c:210 msgid "pending kernel upgrade" msgstr "Kernel Upgrade vorhanden" #: src/ui.c:211 msgid "unknown kernel upgrade state" msgstr "Kernel Upgrade-Status unbekannt" #: src/ui.c:212 msgid "this is a virtualized machine" msgstr "Host ist virtualisiert" #: src/ui.c:214 msgid "this machine is part of a cluster" msgstr "Host ist Teil eines Clusters" #: src/ui.c:589 msgid "FLAG" msgstr "FLAG" #: src/ui.c:590 src/ui.c:603 msgid "DESCRIPTION" msgstr "BESCHREIBUNG" #: src/ui.c:602 msgid "KEY" msgstr "TASTE" #: src/ui.c:651 msgid "HOST DETAILS" msgstr "HOST DETAILS" #: src/ui.c:654 msgid "Group:" msgstr "Gruppe:" #: src/ui.c:656 msgid "Hostname:" msgstr "Hostname:" #: src/ui.c:659 msgid "Comment:" msgstr "Kommentar:" #: src/ui.c:663 msgid "Machine Type:" msgstr "Rechner Typ:" #: src/ui.c:667 msgid "Architecture:" msgstr "Architektur:" #: src/ui.c:671 msgid "UUID:" msgstr "UUID:" #: src/ui.c:675 msgid "Forbidden:" msgstr "Untersagt:" #: src/ui.c:679 msgid "refresh" msgstr "Host(s) aktualisieren" #: src/ui.c:686 msgid "upgrade" msgstr "Host(s) upgraden" #: src/ui.c:693 msgid "install" msgstr "Paket installieren" #: src/ui.c:701 msgid "Distri:" msgstr "Distri:" #: src/ui.c:707 msgid "Release:" msgstr "Release:" #: src/ui.c:711 msgid "Kernel name:" msgstr "Kernel-Name:" #: src/ui.c:715 msgid "Kernel version:" msgstr "Kernel-Version:" #: src/ui.c:720 msgid "(pending ABI compatible upgrade)" msgstr "(ABI-kompatibles Upgrade vorhanden)" #: src/ui.c:723 msgid "(pending upgrade)" msgstr "(Upgrade vorhanden)" #: src/ui.c:738 msgid "Clusters: " msgstr "Cluster: " #: src/ui.c:750 msgid "Packages: " msgstr "Pakete: " #: src/ui.c:761 msgid "BROKEN PACKAGES" msgstr "DEFEKTE PAKETE" #: src/ui.c:782 msgid "UPDATE PACKAGES" msgstr "UPDATE PAKETE" #: src/ui.c:803 msgid "HOLD BACK PACKAGES" msgstr "ZURÜCKGEHALTENE PAKETE" #: src/ui.c:823 msgid "EXTRA PACKAGES" msgstr "EXTRA PAKETE" #: src/ui.c:843 msgid "INSTALLED PACKAGES" msgstr "INSTALLIERTE PAKETE" #: src/ui.c:909 msgid "FAILURE DIAGNOSTIC" msgstr "FEHLER DIAGNOSE" #: src/ui.c:949 #, fuzzy, c-format msgid " [Oldest: %x %X]" msgstr " [Ältester: %D %H:%M]" #: src/ui.c:1035 msgid "c" msgstr "a" #: src/ui.c:1035 msgid "C" msgstr "A" #: src/ui.c:1038 msgid "y" msgstr "j" #: src/ui.c:1038 msgid "Y" msgstr "J" #: src/ui.c:1113 msgid "No history data available!" msgstr "Keine Historiendaten verfügbar!" #: src/ui.c:1119 #, c-format msgid "History of %s (%d entry available)" msgstr "Historie von %s (%d Eintrag verfügbar)" #: src/ui.c:1120 #, c-format msgid "History of %s (%d entries available)" msgstr "Historie von %s (%d Einträge verfügbar)" #: src/ui.c:1202 msgid "replay terminated, press any key to continue" msgstr "Wiedergabe beendet, bel. Taste drücken um fortzufahren" #: src/ui.c:1261 src/ui.c:1292 #, c-format msgid "%d Hosts in status \"%s\"" msgstr "%d Hosts mit Status \"%s\"" #: src/ui.c:1264 src/ui.c:1295 #, c-format msgid "%d Host in status \"%s\"" msgstr "%d Host in Status \"%s\"" #: src/ui.c:1396 #, c-format msgid "%d Updates required" msgstr "%d Updates benötigt" #: src/ui.c:1399 #, c-format msgid "%d Update required" msgstr "%d Update benötigt" #: src/ui.c:1404 #, c-format msgid "No update required" msgstr "Kein Update nötig" #: src/ui.c:1409 #, c-format msgid "%d Broken packages" msgstr "%d defekte Pakete" #: src/ui.c:1412 #, c-format msgid "%d Broken package" msgstr "%d defektes Paket" #: src/ui.c:1425 #, c-format msgid "%d sessions running" msgstr "%d Sitzungen aktiv" #: src/ui.c:1428 #, c-format msgid "%d session running" msgstr "%d Sitzung aktiv" #: src/ui.c:1434 #, c-format msgid "Status is unknown" msgstr "Status ist unbekannt" #: src/ui.c:1437 #, c-format msgid "Error: %s" msgstr "Fehler: %s" #: src/ui.c:1443 msgid " - host locked by another process" msgstr " - Host durch anderen Prozess gesperrt" #: src/ui.c:1499 #, c-format msgid "Running session %s [%5d]:" msgstr "Laufende Sitzung %s [%d]:" #: src/ui.c:1525 src/ui.c:1598 msgid "%D %H:%M " msgstr "%D %H:%M " #: src/ui.c:1528 src/ui.c:1601 msgid "Attached" msgstr "verbunden" #: src/ui.c:1528 src/ui.c:1601 msgid "Detached" msgstr "nicht verbunden" #: src/ui.c:1540 msgid "Could not read session dump." msgstr "Kann Sitzungsausgabe nicht lesen." #: src/ui.c:1543 msgid "attached" msgstr "verbunden" #: src/ui.c:1543 msgid "detached" msgstr "nicht verbunden" #: src/ui.c:1872 msgid "Maintainer name:" msgstr "Betreuer-Name:" #: src/ui.c:2493 msgid "Search: " msgstr "Suche: " #: src/ui.c:2666 msgid "Matches:" msgstr "Treffer:" #: src/ui.c:2683 msgid "Matches: -" msgstr "Treffer: -" #: src/ui.c:2751 msgid "Internal error: unhandled TCL TCLM_STRING mapping!" msgstr "Interner Fehler: unbekannte TCL TCLM_STRING Zuordnung!" #: src/ui.c:2766 msgid "Internal error: unhandled TCL TCLM_INT mapping!" msgstr "Interner Fehler: unbekannte TCL TCLM_INT Zuordnung!" #: src/ui.c:2776 msgid "Internal error: unknown TCL mapping type!" msgstr "Interner Fehler: unbekannter TCL Zuordnungstyp!" #: src/ui.c:2844 msgid "Scalars:" msgstr "Skalare:" #: src/ui.c:2866 msgid "Arrays:" msgstr "Felder:" #: src/ui.c:2887 msgid "Examples:" msgstr "Beispiele:" #: src/ui.c:2900 msgid "Enter filter expression:" msgstr "Filterausdruck:" #: src/ui.c:2934 #, c-format msgid "An error at %s:%d has been detected [Less/ignore/connect]: " msgstr "Ein Fehler bei %s:%d wurde gefunden [Less/ignore/connect]: " #: src/ui.c:2936 #, c-format msgid "An error at %s has been detected [Less/ignore/connect]: " msgstr "Ein Fehler bei %s wurde gefunden [Less/ignore/connect]: " # c-format #: src/ui.c:3113 #, c-format msgid "Refresh %d tagged hosts? [y/N]: " msgstr "Update von %d markierten Hosts? [j/N]: " #: src/ui.c:3148 msgid "tag-" msgstr "tag-" #: src/ui.c:3162 src/ui.c:3188 src/ui.c:3316 msgid "There are running sessions on this host! Continue? [y/N]: " msgstr "Host hat aktive Sitzungen! Weiter? [j/N]: " #: src/ui.c:3211 msgid "Run update for the whole category? [y/N]: " msgstr "Die ganze Kategorie updaten? [j/N]: " #: src/ui.c:3212 msgid "Run update for the whole group? [y/N]: " msgstr "Die ganze Gruppe updaten? [j/N]: " #: src/ui.c:3256 #, c-format msgid "Run update for %d tagged and updatable hosts? [y/N]: " msgstr "Update von %d markierten Hosts? [j/N]: " #: src/ui.c:3286 #, c-format msgid "Install package `%s' [y/N]: " msgstr "Paket '%s' installieren [j/N]: " #: src/ui.c:3292 src/ui.c:3321 src/ui.c:3337 msgid "Install package: " msgstr "Paket installieren: " #: src/ui.c:3341 msgid "Run install for the whole category? [y/N]: " msgstr "Installation für die ganze Kategorie durchführen? [j/N]: " #: src/ui.c:3342 msgid "Run install for the whole group? [y/N]: " msgstr "Installation für die ganze Gruppe durchführen? [j/N]: " #: src/ui.c:3387 #, c-format msgid "Install package on %d tagged hosts: " msgstr "Paket auf %d markierte Hosts installieren: " #: src/ui.c:3415 msgid "Tag hosts matching: " msgstr "Markiere Hosts mit: " #: src/ui.c:3416 msgid "Untag hosts matching: " msgstr "Demarkiere Hosts mit: " #: src/ui.c:3491 #, c-format msgid "Attach host %s session %d (%d %s left) [Y/n/c]: " msgstr "Verbinde mit Host %s Sitzung %d (%d %s übrig) [J/n/a]: " #: src/ui.c:3493 msgid "session" msgstr "Sitzungen" #: src/ui.c:3493 msgid "sessions" msgstr "Sitzungen" #: src/ui.c:3565 msgid "Already attached - share session? [y/N]: " msgstr "Bereits verbunden - geteilte Sitzung? [j/N]: " #: src/ui.c:3593 msgid "Session dumps enabled." msgstr "Sitzungsausgabe aktiviert." #: src/ui.c:3595 msgid "Session dumps disabled." msgstr "Sitzungsausgabe deaktiviert." #: src/ui.c:3693 #, c-format msgid "There are %d hosts in status refresh state, quit apt-dater? [y/N]: " msgstr "Es werden %d Hosts aktualisiert, apt-dater beenden? [j/N]: " #: src/ui.c:3696 #, c-format msgid "There is %d host in status refresh state, quit apt-dater? [y/N]: " msgstr "Es wird %d Host aktualisiert, apt-dater beenden? [j/N]: " apt-dater/po/.gitignore0000664000175000017500000000023615131461765013145 0ustar memeMakefile.in.in Makevars.template Rules-quot boldquot.sed en@boldquot.header en@quot.header insert-header.sin quot.sed remove-potcdate.sed remove-potcdate.sin apt-dater/po/LINGUAS0000664000175000017500000000001115131461765012171 0ustar memede it pt apt-dater/po/Makevars0000664000175000017500000000646615131461765012664 0ustar meme# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = $(PACKAGE) # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = IBH IT-Service GmbH # This tells whether or not to prepend "GNU " prefix to the package # name that gets inserted into the header of the $(DOMAIN).pot file. # Possible values are "yes", "no", or empty. If it is empty, try to # detect it automatically by scanning the files in $(top_srcdir) for # "GNU packagename" string. PACKAGE_GNU = # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = apt-dater@ibh.de # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = no # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = # These options get passed to msginit. # If you want to disable line wrapping when writing PO files, add # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and # MSGINIT_OPTIONS. MSGINIT_OPTIONS = # This tells whether or not to regenerate a PO file when $(DOMAIN).pot # has changed. Possible values are "yes" and "no". Set this to no if # the POT file is checked in the repository and the version control # program ignores timestamps. PO_DEPENDS_ON_POT = yes # This tells whether or not to forcibly update $(DOMAIN).pot and # regenerate PO files on "make dist". Possible values are "yes" and # "no". Set this to no if the POT file and PO files are maintained # externally. DIST_DEPENDS_ON_UPDATE_PO = yes apt-dater/po/pt.po0000664000175000017500000003734015131461765012146 0ustar meme# Translations of apt-dater to European Portuguese # Copyright (C) 2014 the apt-dater's copyright holder # This file is distributed under the same license as the apt-dater package. # # Américo Monteiro , 2014. msgid "" msgstr "" "Project-Id-Version: apt-dater 1.0.0-1\n" "Report-Msgid-Bugs-To: apt-dater@ibh.de\n" "POT-Creation-Date: 2014-10-28 18:26+0100\n" "PO-Revision-Date: 2014-10-31 17:48+0000\n" "Last-Translator: Américo Monteiro \n" "Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 1.4\n" #: src/apt-dater.c:79 src/apt-dater.c:82 src/apt-dater.c:121 src/keyfiles.c:46 #: src/keyfiles.c:60 src/ui.c:3496 msgid "Out of memory." msgstr "Memória insuficiente." #: src/apt-dater.c:88 #, c-format msgid "Failed to create initial configuration file %s." msgstr "Falha ao criar ficheiro de configuração inicial %s." #: src/apt-dater.c:104 msgid "Sorry, apt-dater was compiled w/o report feature!" msgstr "Desculpe, o apt-dater foi compilado sem a funcionalidade de reportar!" #: src/apt-dater.c:114 #, c-format msgid "Usage: %s [-(c config|v|[n]r)]\n" msgstr "Utilização: %s [-(c config|v|[n]r)]\n" #: src/apt-dater.c:116 #, c-format msgid "Usage: %s [-(c config|v)]\n" msgstr "Utilização: %s [-(c config|v)]\n" #: src/apt-dater.c:126 src/apt-dater.c:131 #, c-format msgid "Error on loading config file %s\n" msgstr "Erro ao carregar ficheiro de configuração %s\n" #: src/apt-dater.c:184 msgid "Cannot open your terminal /proc/self/fd/0 - please check." msgstr "Incapaz de abrir o seu terminal /proc/self/fd/0 - por favor verifique." #: src/colors.c:150 msgid "Wrong color definition!" msgstr "Definição de cor errada!" #: src/keyfiles.c:50 #, c-format msgid "Creating default config file %s" msgstr "A criar ficheiro de configuração predefinido %s" #: src/keyfiles.c:52 #, c-format msgid "Could not write to file %s." msgstr "Incapaz de escrever no ficheiro %s." #: src/keyfiles.c:62 #, c-format msgid "Mandatory config file %s does not exist!" msgstr "O ficheiro de configuração obrigatório %s não existe!" #: src/lock.c:53 msgid "Can't get the name of the lock file!" msgstr "Incapaz de obter o nome do ficheiro lock!" #: src/lock.c:74 #, c-format msgid "Failed to get lockfile %s: %s" msgstr "Falha ao obter ficheiro lock %s: %s" #: src/lock.c:78 #, c-format msgid "Can't lock to file %s because function flock() is missing!" msgstr "" "Incapaz de trancar o ficheiro %s porque a função flock() está em falta!" #: src/report.c:55 msgid "Error creating the xml output." msgstr "Erro ao criar os resultados em xml." #: src/report.c:60 #, c-format msgid "apt-dater is refreshing %d hosts, please standby...\n" msgstr "O apt-dater está a refrescar %d máquinas, por favor aguarde...\n" #: src/stats.c:541 msgid "Auto refresh triggered..." msgstr "Refrescamento automático activado..." #: src/ui.c:52 msgid "Updates pending" msgstr "Actualizações pendentes" #: src/ui.c:53 msgid "Up to date" msgstr "Actualizado" #: src/ui.c:54 msgid "Broken packages" msgstr "Pacotes quebrados" #: src/ui.c:55 src/ui.c:1417 #, c-format msgid "Refresh required" msgstr "Pedido de refrescamento" #: src/ui.c:56 src/ui.c:1420 #, c-format msgid "In refresh" msgstr "A refrescar" #: src/ui.c:57 msgid "Sessions" msgstr "Sessões" #: src/ui.c:59 msgid "Filtered" msgstr "Filtrado" #: src/ui.c:61 msgid "Unknown" msgstr "Desconhecido" #: src/ui.c:154 msgid "" msgstr "" #: src/ui.c:154 src/ui.c:155 msgid "shrink node" msgstr "encolher nó" #: src/ui.c:156 msgid "" msgstr "" #: src/ui.c:156 src/ui.c:157 msgid "expand node" msgstr "expandir nó" #: src/ui.c:158 msgid "" msgstr "" #: src/ui.c:158 src/ui.c:159 src/ui.c:160 src/ui.c:169 msgid "shrink/expand node" msgstr "encolher/expandir nó" #: src/ui.c:159 msgid "" msgstr "" #: src/ui.c:160 msgid "" msgstr "" #: src/ui.c:161 msgid "" msgstr "" #: src/ui.c:161 src/ui.c:162 msgid "move up" msgstr "mover para cima" #: src/ui.c:163 msgid "" msgstr "" #: src/ui.c:163 src/ui.c:164 msgid "move down" msgstr "mover para baixo" #: src/ui.c:165 msgid "" msgstr "" #: src/ui.c:165 msgid "move to the top" msgstr "mover para o topo" #: src/ui.c:166 msgid "" msgstr "" #: src/ui.c:166 msgid "move to the end" msgstr "mover para o fim" #: src/ui.c:167 msgid "" msgstr "" #: src/ui.c:167 msgid "previous page" msgstr "página anterior" #: src/ui.c:168 msgid "" msgstr "" #: src/ui.c:168 msgid "next page" msgstr "próxima pagina" #: src/ui.c:170 msgid "quit" msgstr "terminar programa" #: src/ui.c:171 msgid "help" msgstr "ajuda" #: src/ui.c:172 msgid "search host" msgstr "procurar máquina" #: src/ui.c:174 msgid "filter hosts" msgstr "filtrar máquinas" #: src/ui.c:176 msgid "attach session" msgstr "anexar sessão" #: src/ui.c:177 msgid "connect host" msgstr "ligar a máquina" #: src/ui.c:178 msgid "file transfer" msgstr "transferência de ficheiro" #: src/ui.c:179 msgid "toggle dumps" msgstr "trocar despejos" #: src/ui.c:180 msgid "refresh host" msgstr "refrescar máquina" #: src/ui.c:181 msgid "failure diagnostic" msgstr "diagnóstico de falha" #: src/ui.c:182 msgid "install pkg" msgstr "instala pacote" #: src/ui.c:183 msgid "upgrade host(s)" msgstr "actualizar máquina(s)" #: src/ui.c:184 msgid "host details" msgstr "detalhes da máquina" #: src/ui.c:186 msgid "host history" msgstr "histórico da máquina" #: src/ui.c:187 msgid "play" msgstr "reproduzir" #: src/ui.c:188 msgid "display with less" msgstr "mostrar com o less" #: src/ui.c:190 msgid "next detached session" msgstr "próxima sessão desanexada" #: src/ui.c:191 msgid "cycle detached sessions" msgstr "ciclo a sessões desanexadas" #: src/ui.c:192 msgid "tag current host" msgstr "marcar máquina actual" #: src/ui.c:193 msgid "tag all hosts matching" msgstr "marcar todas as máquinas correspondentes" #: src/ui.c:194 msgid " ~c tag by codename" msgstr " ~c marcar por nome de código" #: src/ui.c:195 msgid " ~d tag by distribution" msgstr " ~d marcar por distribuição" #: src/ui.c:196 msgid " ~f tag by host flags" msgstr " ~f marcar por bandeiras da máquina" #: src/ui.c:197 msgid " ~g tag by group" msgstr " ~g marcar por grupo" #: src/ui.c:198 msgid " ~p tag by packages" msgstr " ~p marcar por pacotes" #: src/ui.c:199 msgid " ~u tag by updates" msgstr " ~u marcar por actualizações" #: src/ui.c:200 msgid " ~A tag all hosts" msgstr " ~A marcar todas as máquinas" #: src/ui.c:201 msgid "untag all hosts matching" msgstr "desmarcar todas as máquinas correspondentes" #: src/ui.c:202 msgid "apply next function to tagged hosts" msgstr "aplicar a próxima função às máquinas marcadas" #: src/ui.c:207 msgid "some packages are kept back" msgstr "alguns pacotes forma mantidos na versão anterior" #: src/ui.c:208 msgid "extra packages are installed" msgstr "são instalados pacotes extra" #: src/ui.c:209 msgid "pending kernel upgrade (ABI compatible)" msgstr "pendente de actualização de kernel (compatível com ABI)" #: src/ui.c:210 msgid "pending kernel upgrade" msgstr "pendente de actualização de kernel" #: src/ui.c:211 msgid "unknown kernel upgrade state" msgstr "estado desconhecido de actualização de kernel" #: src/ui.c:212 msgid "this is a virtualized machine" msgstr "esta é uma máquina virtualizada" #: src/ui.c:214 msgid "this machine is part of a cluster" msgstr "esta máquina é parte de um cluster" #: src/ui.c:589 msgid "FLAG" msgstr "BANDEIRA" #: src/ui.c:590 src/ui.c:603 msgid "DESCRIPTION" msgstr "DESCRIÇÃO" #: src/ui.c:602 msgid "KEY" msgstr "CHAVE" #: src/ui.c:651 msgid "HOST DETAILS" msgstr "DETALHES DA MÁQUINA ANFITRIÃ" #: src/ui.c:654 msgid "Group:" msgstr "Grupo:" #: src/ui.c:656 msgid "Hostname:" msgstr "Nome da Máquina:" #: src/ui.c:659 msgid "Comment:" msgstr "Comentário:" #: src/ui.c:663 msgid "Machine Type:" msgstr "Tipo de Máquina:" #: src/ui.c:667 msgid "Architecture:" msgstr "Arquitectura:" #: src/ui.c:671 msgid "UUID:" msgstr "UUID:" #: src/ui.c:675 msgid "Forbidden:" msgstr "Proibido:" #: src/ui.c:679 msgid "refresh" msgstr "refrescar" #: src/ui.c:686 msgid "upgrade" msgstr "actualizar" #: src/ui.c:693 msgid "install" msgstr "instalar" #: src/ui.c:701 msgid "Distri:" msgstr "Distribuição:" #: src/ui.c:707 msgid "Release:" msgstr "Lançamento:" #: src/ui.c:711 msgid "Kernel name:" msgstr "Nome do kernel:" #: src/ui.c:715 msgid "Kernel version:" msgstr "Versão do kernel:" #: src/ui.c:720 msgid "(pending ABI compatible upgrade)" msgstr "(pendente de actualização compatível com ABI)" #: src/ui.c:723 msgid "(pending upgrade)" msgstr "(pendente de actualização)" #: src/ui.c:738 msgid "Clusters: " msgstr "Clusters: " #: src/ui.c:750 msgid "Packages: " msgstr "Pacotes: " #: src/ui.c:761 msgid "BROKEN PACKAGES" msgstr "PACOTES QUEBRADOS" #: src/ui.c:782 msgid "UPDATE PACKAGES" msgstr "ACTUALIZAR PACOTES" #: src/ui.c:803 msgid "HOLD BACK PACKAGES" msgstr "MANTER PACOTES NA VERSÃO ACTUAL" #: src/ui.c:823 msgid "EXTRA PACKAGES" msgstr "PACOTES EXTRA" #: src/ui.c:843 msgid "INSTALLED PACKAGES" msgstr "PACOTES INSTALADOS" #: src/ui.c:909 msgid "FAILURE DIAGNOSTIC" msgstr "DIAGNÓSTICO DE FALHA" #: src/ui.c:949 #, c-format msgid " [Oldest: %x %X]" msgstr " [Mais antigo: %x %X]" #: src/ui.c:1035 msgid "c" msgstr "c" #: src/ui.c:1035 msgid "C" msgstr "C" #: src/ui.c:1038 msgid "y" msgstr "y" #: src/ui.c:1038 msgid "Y" msgstr "Y" #: src/ui.c:1113 msgid "No history data available!" msgstr "Nenhuns dados de histórico disponíveis!" #: src/ui.c:1119 #, c-format msgid "History of %s (%d entry available)" msgstr "Histórico de %s (%d entrada disponível)" #: src/ui.c:1120 #, c-format msgid "History of %s (%d entries available)" msgstr "Histórico de %s (%d entradas disponíveis)" #: src/ui.c:1202 msgid "replay terminated, press any key to continue" msgstr "repetição terminada, carregue em qualquer tecla para continuar" #: src/ui.c:1261 src/ui.c:1292 #, c-format msgid "%d Hosts in status \"%s\"" msgstr "%d Máquinas no status \"%s\"" #: src/ui.c:1264 src/ui.c:1295 #, c-format msgid "%d Host in status \"%s\"" msgstr "%d Máquina no status \"%s\"" #: src/ui.c:1396 #, c-format msgid "%d Updates required" msgstr "%d Actualizações requeridas" #: src/ui.c:1399 #, c-format msgid "%d Update required" msgstr "%d Actualização requerida" #: src/ui.c:1404 #, c-format msgid "No update required" msgstr "Nenhuma actualização requerida" #: src/ui.c:1409 #, c-format msgid "%d Broken packages" msgstr "%d Pacotes quebrados" #: src/ui.c:1412 #, c-format msgid "%d Broken package" msgstr "%d Pacote quebrado" #: src/ui.c:1425 #, c-format msgid "%d sessions running" msgstr "%d sessões a correr" #: src/ui.c:1428 #, c-format msgid "%d session running" msgstr "%d sessão a correr" #: src/ui.c:1434 #, c-format msgid "Status is unknown" msgstr "O status é desconhecido" #: src/ui.c:1437 #, c-format msgid "Error: %s" msgstr "Erro: %s" #: src/ui.c:1443 msgid " - host locked by another process" msgstr " - máquina trancada por outro processo" #: src/ui.c:1499 #, c-format msgid "Running session %s [%5d]:" msgstr "Sessão a correr %s [%5d]:" #: src/ui.c:1525 src/ui.c:1598 msgid "%D %H:%M " msgstr "%D %H:%M " #: src/ui.c:1528 src/ui.c:1601 msgid "Attached" msgstr "Anexado" #: src/ui.c:1528 src/ui.c:1601 msgid "Detached" msgstr "Desanexado" #: src/ui.c:1540 msgid "Could not read session dump." msgstr "Incapaz de ler o despejo da sessão." #: src/ui.c:1543 msgid "attached" msgstr "anexado" #: src/ui.c:1543 msgid "detached" msgstr "desanexado" #: src/ui.c:1872 msgid "Maintainer name:" msgstr "Nome do mantenedor:" #: src/ui.c:2493 msgid "Search: " msgstr "Procurar: " #: src/ui.c:2666 msgid "Matches:" msgstr "Correspondências:" #: src/ui.c:2683 msgid "Matches: -" msgstr "Correspondências: -" #: src/ui.c:2751 msgid "Internal error: unhandled TCL TCLM_STRING mapping!" msgstr "Erro interno: mapeamento TCL TCLM_STRING não lidado!" #: src/ui.c:2766 msgid "Internal error: unhandled TCL TCLM_INT mapping!" msgstr "Erro interno: mapeamento TCL TCLM_INT não lidado!" #: src/ui.c:2776 msgid "Internal error: unknown TCL mapping type!" msgstr "Erro interno: tipo de mapeamento TCL desconhecido!" #: src/ui.c:2844 msgid "Scalars:" msgstr "Escalares:" #: src/ui.c:2866 msgid "Arrays:" msgstr "Matrizes:" #: src/ui.c:2887 msgid "Examples:" msgstr "Exemplos:" #: src/ui.c:2900 msgid "Enter filter expression:" msgstr "Inserir expressão de filtro:" #: src/ui.c:2934 #, c-format msgid "An error at %s:%d has been detected [Less/ignore/connect]: " msgstr "Foi detectado um erro em %s:%d [Menos/ignorar/ligar]: " #: src/ui.c:2936 #, c-format msgid "An error at %s has been detected [Less/ignore/connect]: " msgstr "Foi detectado um erro em %s [Menos/ignorar/ligar]: " # c-format #: src/ui.c:3113 #, c-format msgid "Refresh %d tagged hosts? [y/N]: " msgstr "Refrescar %d máquinas marcadas? [s/N]: " #: src/ui.c:3148 msgid "tag-" msgstr "marca-" #: src/ui.c:3162 src/ui.c:3188 src/ui.c:3316 msgid "There are running sessions on this host! Continue? [y/N]: " msgstr "Existem sessões a correr nesta máquina! Continuar? [s/N]: " #: src/ui.c:3211 msgid "Run update for the whole category? [y/N]: " msgstr "Correr a actualização para a categoria inteira? [s/N]: " #: src/ui.c:3212 msgid "Run update for the whole group? [y/N]: " msgstr "Correr a actualização para o grupo inteiro? [s/N]: " #: src/ui.c:3256 #, c-format msgid "Run update for %d tagged and updatable hosts? [y/N]: " msgstr "" "Correr a actualização para %d maquinas marcadas e actualizáveis? [s/N]: " #: src/ui.c:3286 #, c-format msgid "Install package `%s' [y/N]: " msgstr "Instalar pacote `%s' [s/N]: " #: src/ui.c:3292 src/ui.c:3321 src/ui.c:3337 msgid "Install package: " msgstr "Instalar pacote:" #: src/ui.c:3341 msgid "Run install for the whole category? [y/N]: " msgstr "Correr a instalação para a categoria inteira? [s/N]: " #: src/ui.c:3342 msgid "Run install for the whole group? [y/N]: " msgstr "Correr a instalação para o grupo inteiro? [s/N]: " #: src/ui.c:3387 #, c-format msgid "Install package on %d tagged hosts: " msgstr "Instalar pacote em %d máquinas marcadas: " #: src/ui.c:3415 msgid "Tag hosts matching: " msgstr "Marcar máquinas que correspondem a:" #: src/ui.c:3416 msgid "Untag hosts matching: " msgstr "Desmarcar máquinas que correspondem a:" #: src/ui.c:3491 #, c-format msgid "Attach host %s session %d (%d %s left) [Y/n/c]: " msgstr "Anexar máquina %s sessão %d (%d %s falta) [S/n/c]: " #: src/ui.c:3493 msgid "session" msgstr "sessão" #: src/ui.c:3493 msgid "sessions" msgstr "sessões" #: src/ui.c:3565 msgid "Already attached - share session? [y/N]: " msgstr "Já anexado - partilhar sessão [s/N]: " #: src/ui.c:3593 msgid "Session dumps enabled." msgstr "Despejos de sessão activos." #: src/ui.c:3595 msgid "Session dumps disabled." msgstr "Despejos de sessão inactivos." #: src/ui.c:3693 #, c-format msgid "There are %d hosts in status refresh state, quit apt-dater? [y/N]: " msgstr "" "Estão %d máquinas no estado de refrescar o status, terminar o apt-dater? [s/" "N]: " #: src/ui.c:3696 #, c-format msgid "There is %d host in status refresh state, quit apt-dater? [y/N]: " msgstr "" "Está %d máquina no estado de refrescar o status, terminar o apt-dater? [s/" "N]: " #~ msgid "running kernel is not the latest (reboot required)" #~ msgstr "" #~ "o kernel a correr não é o mais recente (reiniciar do sistema requerido)" #~ msgid "a selfbuild kernel is running" #~ msgstr "está a correr um kernel de compilação própria" #~ msgid "(reboot required)" #~ msgstr "(re-iniciação do sistema necessária)" #~ msgid "(selfbuild kernel)" #~ msgstr "(kernel de compilação própria)" apt-dater/po/apt-dater.pot0000664000175000017500000002552415131461765013571 0ustar meme# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR IBH IT-Service GmbH # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: apt-dater 1.0.0\n" "Report-Msgid-Bugs-To: apt-dater@ibh.de\n" "POT-Creation-Date: 2014-10-28 18:26+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: src/apt-dater.c:79 src/apt-dater.c:82 src/apt-dater.c:121 src/keyfiles.c:46 #: src/keyfiles.c:60 src/ui.c:3496 msgid "Out of memory." msgstr "" #: src/apt-dater.c:88 #, c-format msgid "Failed to create initial configuration file %s." msgstr "" #: src/apt-dater.c:104 msgid "Sorry, apt-dater was compiled w/o report feature!" msgstr "" #: src/apt-dater.c:114 #, c-format msgid "Usage: %s [-(c config|v|[n]r)]\n" msgstr "" #: src/apt-dater.c:116 #, c-format msgid "Usage: %s [-(c config|v)]\n" msgstr "" #: src/apt-dater.c:126 src/apt-dater.c:131 #, c-format msgid "Error on loading config file %s\n" msgstr "" #: src/apt-dater.c:184 msgid "Cannot open your terminal /proc/self/fd/0 - please check." msgstr "" #: src/colors.c:150 msgid "Wrong color definition!" msgstr "" #: src/keyfiles.c:50 #, c-format msgid "Creating default config file %s" msgstr "" #: src/keyfiles.c:52 #, c-format msgid "Could not write to file %s." msgstr "" #: src/keyfiles.c:62 #, c-format msgid "Mandatory config file %s does not exist!" msgstr "" #: src/lock.c:53 msgid "Can't get the name of the lock file!" msgstr "" #: src/lock.c:74 #, c-format msgid "Failed to get lockfile %s: %s" msgstr "" #: src/lock.c:78 #, c-format msgid "Can't lock to file %s because function flock() is missing!" msgstr "" #: src/report.c:55 msgid "Error creating the xml output." msgstr "" #: src/report.c:60 #, c-format msgid "apt-dater is refreshing %d hosts, please standby...\n" msgstr "" #: src/stats.c:541 msgid "Auto refresh triggered..." msgstr "" #: src/ui.c:52 msgid "Updates pending" msgstr "" #: src/ui.c:53 msgid "Up to date" msgstr "" #: src/ui.c:54 msgid "Broken packages" msgstr "" #: src/ui.c:55 src/ui.c:1417 #, c-format msgid "Refresh required" msgstr "" #: src/ui.c:56 src/ui.c:1420 #, c-format msgid "In refresh" msgstr "" #: src/ui.c:57 msgid "Sessions" msgstr "" #: src/ui.c:59 msgid "Filtered" msgstr "" #: src/ui.c:61 msgid "Unknown" msgstr "" #: src/ui.c:154 msgid "" msgstr "" #: src/ui.c:154 src/ui.c:155 msgid "shrink node" msgstr "" #: src/ui.c:156 msgid "" msgstr "" #: src/ui.c:156 src/ui.c:157 msgid "expand node" msgstr "" #: src/ui.c:158 msgid "" msgstr "" #: src/ui.c:158 src/ui.c:159 src/ui.c:160 src/ui.c:169 msgid "shrink/expand node" msgstr "" #: src/ui.c:159 msgid "" msgstr "" #: src/ui.c:160 msgid "" msgstr "" #: src/ui.c:161 msgid "" msgstr "" #: src/ui.c:161 src/ui.c:162 msgid "move up" msgstr "" #: src/ui.c:163 msgid "" msgstr "" #: src/ui.c:163 src/ui.c:164 msgid "move down" msgstr "" #: src/ui.c:165 msgid "" msgstr "" #: src/ui.c:165 msgid "move to the top" msgstr "" #: src/ui.c:166 msgid "" msgstr "" #: src/ui.c:166 msgid "move to the end" msgstr "" #: src/ui.c:167 msgid "" msgstr "" #: src/ui.c:167 msgid "previous page" msgstr "" #: src/ui.c:168 msgid "" msgstr "" #: src/ui.c:168 msgid "next page" msgstr "" #: src/ui.c:170 msgid "quit" msgstr "" #: src/ui.c:171 msgid "help" msgstr "" #: src/ui.c:172 msgid "search host" msgstr "" #: src/ui.c:174 msgid "filter hosts" msgstr "" #: src/ui.c:176 msgid "attach session" msgstr "" #: src/ui.c:177 msgid "connect host" msgstr "" #: src/ui.c:178 msgid "file transfer" msgstr "" #: src/ui.c:179 msgid "toggle dumps" msgstr "" #: src/ui.c:180 msgid "refresh host" msgstr "" #: src/ui.c:181 msgid "failure diagnostic" msgstr "" #: src/ui.c:182 msgid "install pkg" msgstr "" #: src/ui.c:183 msgid "upgrade host(s)" msgstr "" #: src/ui.c:184 msgid "host details" msgstr "" #: src/ui.c:186 msgid "host history" msgstr "" #: src/ui.c:187 msgid "play" msgstr "" #: src/ui.c:188 msgid "display with less" msgstr "" #: src/ui.c:190 msgid "next detached session" msgstr "" #: src/ui.c:191 msgid "cycle detached sessions" msgstr "" #: src/ui.c:192 msgid "tag current host" msgstr "" #: src/ui.c:193 msgid "tag all hosts matching" msgstr "" #: src/ui.c:194 msgid " ~c tag by codename" msgstr "" #: src/ui.c:195 msgid " ~d tag by distribution" msgstr "" #: src/ui.c:196 msgid " ~f tag by host flags" msgstr "" #: src/ui.c:197 msgid " ~g tag by group" msgstr "" #: src/ui.c:198 msgid " ~p tag by packages" msgstr "" #: src/ui.c:199 msgid " ~u tag by updates" msgstr "" #: src/ui.c:200 msgid " ~A tag all hosts" msgstr "" #: src/ui.c:201 msgid "untag all hosts matching" msgstr "" #: src/ui.c:202 msgid "apply next function to tagged hosts" msgstr "" #: src/ui.c:207 msgid "some packages are kept back" msgstr "" #: src/ui.c:208 msgid "extra packages are installed" msgstr "" #: src/ui.c:209 msgid "pending kernel upgrade (ABI compatible)" msgstr "" #: src/ui.c:210 msgid "pending kernel upgrade" msgstr "" #: src/ui.c:211 msgid "unknown kernel upgrade state" msgstr "" #: src/ui.c:212 msgid "this is a virtualized machine" msgstr "" #: src/ui.c:214 msgid "this machine is part of a cluster" msgstr "" #: src/ui.c:589 msgid "FLAG" msgstr "" #: src/ui.c:590 src/ui.c:603 msgid "DESCRIPTION" msgstr "" #: src/ui.c:602 msgid "KEY" msgstr "" #: src/ui.c:651 msgid "HOST DETAILS" msgstr "" #: src/ui.c:654 msgid "Group:" msgstr "" #: src/ui.c:656 msgid "Hostname:" msgstr "" #: src/ui.c:659 msgid "Comment:" msgstr "" #: src/ui.c:663 msgid "Machine Type:" msgstr "" #: src/ui.c:667 msgid "Architecture:" msgstr "" #: src/ui.c:671 msgid "UUID:" msgstr "" #: src/ui.c:675 msgid "Forbidden:" msgstr "" #: src/ui.c:679 msgid "refresh" msgstr "" #: src/ui.c:686 msgid "upgrade" msgstr "" #: src/ui.c:693 msgid "install" msgstr "" #: src/ui.c:701 msgid "Distri:" msgstr "" #: src/ui.c:707 msgid "Release:" msgstr "" #: src/ui.c:711 msgid "Kernel name:" msgstr "" #: src/ui.c:715 msgid "Kernel version:" msgstr "" #: src/ui.c:720 msgid "(pending ABI compatible upgrade)" msgstr "" #: src/ui.c:723 msgid "(pending upgrade)" msgstr "" #: src/ui.c:738 msgid "Clusters: " msgstr "" #: src/ui.c:750 msgid "Packages: " msgstr "" #: src/ui.c:761 msgid "BROKEN PACKAGES" msgstr "" #: src/ui.c:782 msgid "UPDATE PACKAGES" msgstr "" #: src/ui.c:803 msgid "HOLD BACK PACKAGES" msgstr "" #: src/ui.c:823 msgid "EXTRA PACKAGES" msgstr "" #: src/ui.c:843 msgid "INSTALLED PACKAGES" msgstr "" #: src/ui.c:909 msgid "FAILURE DIAGNOSTIC" msgstr "" #: src/ui.c:949 #, c-format msgid " [Oldest: %x %X]" msgstr "" #: src/ui.c:1035 msgid "c" msgstr "" #: src/ui.c:1035 msgid "C" msgstr "" #: src/ui.c:1038 msgid "y" msgstr "" #: src/ui.c:1038 msgid "Y" msgstr "" #: src/ui.c:1113 msgid "No history data available!" msgstr "" #: src/ui.c:1119 #, c-format msgid "History of %s (%d entry available)" msgstr "" #: src/ui.c:1120 #, c-format msgid "History of %s (%d entries available)" msgstr "" #: src/ui.c:1202 msgid "replay terminated, press any key to continue" msgstr "" #: src/ui.c:1261 src/ui.c:1292 #, c-format msgid "%d Hosts in status \"%s\"" msgstr "" #: src/ui.c:1264 src/ui.c:1295 #, c-format msgid "%d Host in status \"%s\"" msgstr "" #: src/ui.c:1396 #, c-format msgid "%d Updates required" msgstr "" #: src/ui.c:1399 #, c-format msgid "%d Update required" msgstr "" #: src/ui.c:1404 #, c-format msgid "No update required" msgstr "" #: src/ui.c:1409 #, c-format msgid "%d Broken packages" msgstr "" #: src/ui.c:1412 #, c-format msgid "%d Broken package" msgstr "" #: src/ui.c:1425 #, c-format msgid "%d sessions running" msgstr "" #: src/ui.c:1428 #, c-format msgid "%d session running" msgstr "" #: src/ui.c:1434 #, c-format msgid "Status is unknown" msgstr "" #: src/ui.c:1437 #, c-format msgid "Error: %s" msgstr "" #: src/ui.c:1443 msgid " - host locked by another process" msgstr "" #: src/ui.c:1499 #, c-format msgid "Running session %s [%5d]:" msgstr "" #: src/ui.c:1525 src/ui.c:1598 msgid "%D %H:%M " msgstr "" #: src/ui.c:1528 src/ui.c:1601 msgid "Attached" msgstr "" #: src/ui.c:1528 src/ui.c:1601 msgid "Detached" msgstr "" #: src/ui.c:1540 msgid "Could not read session dump." msgstr "" #: src/ui.c:1543 msgid "attached" msgstr "" #: src/ui.c:1543 msgid "detached" msgstr "" #: src/ui.c:1872 msgid "Maintainer name:" msgstr "" #: src/ui.c:2493 msgid "Search: " msgstr "" #: src/ui.c:2666 msgid "Matches:" msgstr "" #: src/ui.c:2683 msgid "Matches: -" msgstr "" #: src/ui.c:2751 msgid "Internal error: unhandled TCL TCLM_STRING mapping!" msgstr "" #: src/ui.c:2766 msgid "Internal error: unhandled TCL TCLM_INT mapping!" msgstr "" #: src/ui.c:2776 msgid "Internal error: unknown TCL mapping type!" msgstr "" #: src/ui.c:2844 msgid "Scalars:" msgstr "" #: src/ui.c:2866 msgid "Arrays:" msgstr "" #: src/ui.c:2887 msgid "Examples:" msgstr "" #: src/ui.c:2900 msgid "Enter filter expression:" msgstr "" #: src/ui.c:2934 #, c-format msgid "An error at %s:%d has been detected [Less/ignore/connect]: " msgstr "" #: src/ui.c:2936 #, c-format msgid "An error at %s has been detected [Less/ignore/connect]: " msgstr "" #: src/ui.c:3113 #, c-format msgid "Refresh %d tagged hosts? [y/N]: " msgstr "" #: src/ui.c:3148 msgid "tag-" msgstr "" #: src/ui.c:3162 src/ui.c:3188 src/ui.c:3316 msgid "There are running sessions on this host! Continue? [y/N]: " msgstr "" #: src/ui.c:3211 msgid "Run update for the whole category? [y/N]: " msgstr "" #: src/ui.c:3212 msgid "Run update for the whole group? [y/N]: " msgstr "" #: src/ui.c:3256 #, c-format msgid "Run update for %d tagged and updatable hosts? [y/N]: " msgstr "" #: src/ui.c:3286 #, c-format msgid "Install package `%s' [y/N]: " msgstr "" #: src/ui.c:3292 src/ui.c:3321 src/ui.c:3337 msgid "Install package: " msgstr "" #: src/ui.c:3341 msgid "Run install for the whole category? [y/N]: " msgstr "" #: src/ui.c:3342 msgid "Run install for the whole group? [y/N]: " msgstr "" #: src/ui.c:3387 #, c-format msgid "Install package on %d tagged hosts: " msgstr "" #: src/ui.c:3415 msgid "Tag hosts matching: " msgstr "" #: src/ui.c:3416 msgid "Untag hosts matching: " msgstr "" #: src/ui.c:3491 #, c-format msgid "Attach host %s session %d (%d %s left) [Y/n/c]: " msgstr "" #: src/ui.c:3493 msgid "session" msgstr "" #: src/ui.c:3493 msgid "sessions" msgstr "" #: src/ui.c:3565 msgid "Already attached - share session? [y/N]: " msgstr "" #: src/ui.c:3593 msgid "Session dumps enabled." msgstr "" #: src/ui.c:3595 msgid "Session dumps disabled." msgstr "" #: src/ui.c:3693 #, c-format msgid "There are %d hosts in status refresh state, quit apt-dater? [y/N]: " msgstr "" #: src/ui.c:3696 #, c-format msgid "There is %d host in status refresh state, quit apt-dater? [y/N]: " msgstr "" apt-dater/TODO0000664000175000017500000000032715131461765011230 0ustar memeTODO ==== - make apt-dater-host package available for more Linux distributions (non apt/dpkg based) - fix UI glitches or better rewrite the code - change UI to show all informations gathered from the clients apt-dater/README0000777000175000017500000000000015131461765012670 2README.mdustar memeapt-dater/configure.ac0000664000175000017500000003221615131461765013030 0ustar meme# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. PACKAGE=apt-dater AC_PREREQ([2.71]) AC_INIT([apt-dater],[1.0.5],[apt-dater@ibh.de]) # include local macros m4_include([m4lib/adl_recursive_eval.m4]) # Recursive eval some vars adl_RECURSIVE_EVAL([$sysconfdir], sysconfdir) adl_RECURSIVE_EVAL([$localstatedir], localstatedir) adl_RECURSIVE_EVAL([$datarootdir], datarootdir) # Environment AC_CANONICAL_HOST AM_INIT_AUTOMAKE([-Wno-portability]) case "${host_os}" in linux*) build_linux="yes" ;; esac AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) AC_CONFIG_SRCDIR([src/apt-dater.c]) AC_CONFIG_SRCDIR([src/adsh.c]) AC_CONFIG_HEADERS([config.h]) AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_REQUIRE_VERSION([0.19.3]) AC_CHECK_HEADERS(locale.h) glib_module="glib-2.0 >= 2.3.6" PKG_CHECK_MODULES(GLIB, "$glib_module", enable_glib_=yes, enable_glib_=no) gio_module="gio-2.0 >= 2.15.0" PKG_CHECK_MODULES(GIO, "$gio_module", enable_gio_=yes, enable_gio_=no) AC_MSG_CHECKING(--enable-debug argument) AC_ARG_ENABLE(debug, [ --enable-debug Enable debug code.], [enable_debug="yes"], [enable_debug="no"]) AC_MSG_RESULT($enable_debug) if test "$enable_debug" = "no"; then AC_DEFINE(NDEBUG, 1, [ Define if you want no debug code included. ]) fi AC_MSG_CHECKING(--enable-xmlreport argument) AC_ARG_ENABLE(xmlreport, [ --enable-xmlreport Include XML reports.], [enable_xmlreport="yes"], [enable_xmlreport="no"]) AC_MSG_RESULT($enable_xmlreport) PKG_CHECK_MODULES(LIBXML2, "libxml-2.0") if test "$enable_xmlreport" = "yes"; then AC_DEFINE(FEAT_XMLREPORT, 1, [ Define if you want XML report support included. ]) fi AC_MSG_CHECKING(--enable-autoref argument) AC_ARG_ENABLE(autoref, [ --enable-autoref Include 'auto refresh' support.], [enable_autoref="yes"], [enable_autoref="no"]) AC_MSG_RESULT($enable_autoref) if test "$enable_autoref" = "yes"; then AC_DEFINE(FEAT_AUTOREF, 1, [ Define if you want 'auto refresh' support included. ]) fi AC_MSG_CHECKING(--enable-history argument) AC_ARG_ENABLE(history, [ --enable-history Include 'history' support.], [enable_history="yes"], [enable_history="no"]) AC_MSG_RESULT($enable_history) if test "$enable_history" = "yes"; then AC_DEFINE(FEAT_HISTORY, 1, [ Define if you want 'history' support included. ]) fi AC_MSG_CHECKING(--enable-clusters argument) AC_ARG_ENABLE(clusters, [ --enable-clusters Include 'clusters' support.], [enable_clusters="yes"], [enable_clusters="no"]) AC_MSG_RESULT($enable_clusters) if test "$enable_clusters" = "yes"; then AC_DEFINE(FEAT_CLUSTERS, 1, [ Define if you want 'clusters' support included. ]) fi AC_MSG_CHECKING(--enable-runcust argument) AC_ARG_ENABLE(runcust, [ --enable-runcust Include 'run custom commands' support.], [enable_runcust="yes"], [enable_runcust="no"]) AC_MSG_RESULT($enable_runcust) if test "$enable_runcust" = "yes"; then AC_DEFINE(FEAT_RUNCUST, 1, [ Define if you want 'run custom commands' support included. ]) fi AM_CONDITIONAL(FEAT_RUNCUST, [test "$enable_runcust" = "yes"]) AC_SUBST(VERSION) AC_SUBST(PACKAGE) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) AC_SUBST(GIO_CFLAGS) AC_SUBST(GIO_LIBS) AC_SUBST(LIBXML2_CFLAGS) AC_SUBST(LIBXML2_LIBS) AC_PROG_INSTALL # Some additional definitions AC_DEFINE(HAVE_COLOR,1,[ Define if your curses library supports color. ]) AC_DEFINE(HAVE_USE_DEFAULT_COLORS,1, [ Define if your curses library supports function use_default_colors. ]) AC_DEFINE(HAVE_FLOCK,1,[ Define if funtion flock avaible. ]) # Checks for programs. AC_PROG_CC # Checks for libraries. if eval "test x$CURSES_LIB_NAME = x"; then AC_CHECK_HEADERS(ncurses.h) if test x$enable_utf8 != xno; then AC_CHECK_LIB(ncursesw, get_wch, [CURSES_LIB="-lncursesw" CPPFLAGS="-I/usr/include/ncursesw $CPPFLAGS" CURSES_LIB_NAME=ncursesw CURSES_LIB_WIDE=yes]) fi if eval "test x$CURSES_LIB_NAME = x"; then AC_CHECK_LIB(ncurses, initscr, [CURSES_LIB="-lncurses" CURSES_LIB_NAME=ncurses]) fi fi if eval "test x$CURSES_LIB_NAME = x"; then # only to log result: AC_CHECK_HEADER(curses.h) if test x$enable_utf8 != xno; then AC_CHECK_LIB(curses, get_wch, [CURSES_LIB="-lcurses" CURSES_LIB_NAME=curses CURSES_LIB_WIDE=yes]) fi if eval "test x$CURSES_LIB_NAME = x"; then AC_CHECK_LIB(curses, initscr, [CURSES_LIB="-lcurses" CURSES_LIB_NAME=curses]) fi fi if eval "test x$CURSES_LIB_NAME = x"; then AC_MSG_ERROR([ *** No curses lib available. Consider getting the official ncurses *** distribution from ftp://ftp.gnu.org/pub/gnu/ncurses if you get *** errors compiling nano.]) else AC_MSG_RESULT([Using $CURSES_LIB_NAME as the curses library]) fi LIBS="$LIBS $CURSES_LIB" AC_CHECK_HEADER([popt.h], [], [AC_MSG_ERROR([Missing popt.h])]) AC_CHECK_LIB([popt], [poptParseArgvString], [], [AC_MSG_ERROR([Missing library popt with poptParseArgvString])]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE # Where to put schema definitions AC_MSG_CHECKING(XMLSCHEMADIR) AC_ARG_VAR([XMLSCHEMADIR], [XML schema directory (default: DATAROOTDIR/xml/schema/apt-dater)]) if test -z "$XMLSCHEMADIR"; then XMLSCHEMADIR="${datarootdir}/xml/schema/apt-dater" fi AC_DEFINE_UNQUOTED(XML_SCHEMA_DIR,"$XMLSCHEMADIR",[ Where apt-dater's DTD schema files are stored. ]) AC_MSG_RESULT($XMLSCHEMADIR) # URI for schema definitions AC_MSG_CHECKING(XMLSCHEMAURI) AC_ARG_VAR([XMLSCHEMAURI], [XML schema URI (default: file://XMLSCHEMADIR)]) if test -z "$XMLSCHEMAURI"; then XMLSCHEMAURI="file://${XMLSCHEMADIR}" fi AC_DEFINE_UNQUOTED(XML_SCHEMA_URI,"$XMLSCHEMAURI",[ URI to apt-dater's DTD schema files. ]) AC_MSG_RESULT($XMLSCHEMAURI) # Check for TCL AC_MSG_CHECKING(--enable-tclfilter argument) AC_ARG_ENABLE(tclfilter, [ --enable-tclfilter Include Tcl interpreter.], , [enable_tclfilter="no"]) AC_MSG_RESULT($enable_tclfilter) if test "$enable_tclfilter" = "yes"; then dnl on FreeBSD tclsh is a silly script, look for tclsh8.[420] AC_MSG_CHECKING(--with-tclsh argument) AC_ARG_WITH(tclsh, [ --with-tclsh=PATH which tclsh to use (default: tclsh8.5)], tclsh_name="$withval"; AC_MSG_RESULT($tclsh_name), tclsh_name="tclsh8.5"; AC_MSG_RESULT(no)) AC_PATH_PROG(cv_path_tcl, $tclsh_name) AC_SUBST(cv_path_tcl) dnl when no specific version specified, also try 8.2 and 8.0 if test "X$cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.4"; then tclsh_name="tclsh8.2" AC_PATH_PROG(cv_path_tcl, $tclsh_name) fi if test "X$cv_path_tcl" = "X" -a $tclsh_name = "tclsh8.2"; then tclsh_name="tclsh8.0" AC_PATH_PROG(cv_path_tcl, $tclsh_name) fi dnl still didn't find it, try without version number if test "X$cv_path_tcl" = "X"; then tclsh_name="tclsh" AC_PATH_PROG(cv_path_tcl, $tclsh_name) fi if test "X$cv_path_tcl" != "X"; then AC_MSG_CHECKING(Tcl version) if echo 'exit [[expr [info tclversion] < 8.0]]' | $cv_path_tcl - ; then tclver=`echo 'puts [[info tclversion]]' | $cv_path_tcl -` AC_MSG_RESULT($tclver - OK); tclloc=`echo 'set l [[info library]];set i [[string last lib $l]];incr i -2;puts [[string range $l 0 $i]]' | $cv_path_tcl -` AC_MSG_CHECKING(for location of Tcl include) if test "x$MACOSX" != "xyes"; then tclinc="$tclloc/include $tclloc/include/tcl $tclloc/include/tcl$tclver /usr/local/include /usr/include /usr/local/include/tcl$tclver /usr/include/tcl$tclver" else dnl For Mac OS X 10.3, use the OS-provided framework location tclinc="/System/Library/Frameworks/Tcl.framework/Headers" fi for try in $tclinc; do if test -f "$try/tcl.h"; then AC_MSG_RESULT($try/tcl.h) TCL_INC=$try break fi done if test -z "$TCL_INC"; then AC_MSG_RESULT() SKIP_TCL=YES fi if test -z "$SKIP_TCL"; then AC_MSG_CHECKING(for location of tclConfig.sh script) if test "x$MACOSX" != "xyes"; then tclcnf=`echo $tclinc | sed s/include/lib/g`" $tclinc" else dnl For Mac OS X 10.3, use the OS-provided framework location tclcnf="/System/Library/Frameworks/Tcl.framework" fi for try in $tclcnf; do if test -f $try/tclConfig.sh; then AC_MSG_RESULT($try/tclConfig.sh) . $try/tclConfig.sh dnl use eval, because tcl 8.2 includes ${TCL_DBGX} TCL_LIBS=`eval echo "$TCL_LIB_SPEC $TCL_LIBS"` dnl Use $TCL_DEFS for -D_THREAD_SAFE et al. But only use the dnl "-D_ABC" items. Watch out for -DFOO=long\ long. TCL_DEFS=`echo $TCL_DEFS | sed -e 's/\\\\ /\\\\X/g' | tr ' ' '\012' | sed -e '/^-[[^D]]/d' -e '/-D[[^_]]/d' -e 's/-D_/ -D_/' | tr '\012' ' ' | sed -e 's/\\\\X/\\\\ /g'` break fi done if test -z "$TCL_LIBS"; then AC_MSG_RESULT() AC_MSG_CHECKING(for Tcl library by myself) tcllib=`echo $tclinc | sed s/include/lib/g` for ext in .so .a ; do for ver in "" $tclver ; do for try in $tcllib ; do trylib=tcl$ver$ext if test -f $try/lib$trylib ; then AC_MSG_RESULT($try/lib$trylib) TCL_LIBS="-L$try -ltcl$ver" if test "`(uname) 2>/dev/null`" = SunOS && uname -r | grep '^5' >/dev/null; then TCL_LIBS="$TCL_LIBS -R $try" fi break 3 fi done done done if test -z "$TCL_LIBS"; then AC_MSG_RESULT() SKIP_TCL=YES fi fi if test -z "$SKIP_TCL"; then AC_DEFINE(FEAT_TCLFILTER, 1, [ Define if you want TCL filter support included. ]) TCL_CFLAGS="-I$TCL_INC $TCL_DEFS" fi fi else AC_MSG_RESULT(too old; need Tcl version 8.0 or later) fi fi fi AC_SUBST(TCL_CFLAGS) AC_SUBST(TCL_LIBS) # Check if `screen' is installed on your system echo -n "checking for screen... " if test -x /usr/bin/screen; then screen_binary="/usr/bin/screen" elif test -x /usr/local/bin/screen; then screen_binary="/usr/local/bin/screen" elif test -x /bin/screen; then screen_binary="/bin/screen" else echo "not found at configure time." echo "Using mostly common systemdefault /usr/bin/screen." screen_binary="/usr/bin/screen" fi AC_DEFINE_UNQUOTED(SCREEN_BINARY,"$screen_binary",[ Where is the command screen located. ]) echo "$screen_binary" # Try to find out where screen stores the sockets echo -n "checking for screen socket path... " if test -x $screen_binary; then screen_sockpath=`build/screen_sockpath "$screen_binary"` if test ! -d "$screen_sockpath"; then echo "Got invalid path '$screen_sockpath' - fallback to common systemdefault." screen_sockpath="/var/run/screen" fi else echo "No screen binary available during build - fallback to common systemdefault." screen_sockpath="/var/run/screen" fi AC_DEFINE_UNQUOTED(SCREEN_SOCKPATH,"$screen_sockpath",[ Where screen stores the sockets. ]) echo "$screen_sockpath" # Check if `tmux' support is enabled. AC_MSG_CHECKING(--enable-tmux argument) AC_ARG_ENABLE(tmux, [ --enable-tmux Enable tmux terminal multiplexer (replaces screen).], [enable_tmux="yes"], [enable_tmux="no"]) AC_MSG_RESULT($enable_tmux) if test "$enable_tmux" = "yes"; then # Check if `tmux' is installed on your system echo -n "checking for tmux... " if test -x /usr/bin/tmux; then tmux_binary="/usr/bin/tmux" elif test -x /usr/local/bin/tmux; then tmux_binary="/usr/local/bin/tmux" elif test -x /bin/tmux; then tmux_binary="/bin/tmux" else echo "not found at configure time." echo "Using mostly common systemdefault /usr/bin/tmux." tmux_binary="/usr/bin/tmux" fi AC_DEFINE_UNQUOTED(TMUX_BINARY,"$tmux_binary",[ Where is the command tmux located. ]) echo "$tmux_binary" AC_DEFINE(FEAT_TMUX, 1, [ Define if you want to use tmux terminal multiplexer. ]) fi AM_CONDITIONAL(FEAT_TMUX, [test "${enable_tmux}" = "yes"]) # Check if `env' is installed on your system (used by history feature) if test "$enable_history" = "yes"; then echo -n "checking for env... " if test -x /usr/bin/env; then AC_DEFINE(ENV_BINARY,"/usr/bin/env",[ Where is the command env located. ]) echo "/usr/bin/env" elif test -x /usr/local/bin/env; then AC_DEFINE(ENV_BINARY,"/usr/local/bin/env",[ Where is the command env located. ]) echo "/usr/local/bin/env" elif test -x /bin/env; then AC_DEFINE(ENV_BINARY,"/bin/env",[ Where is the command env located. ]) echo "/bin/env" else echo "not found at configure time." echo "Using mostly common systemdefault /usr/bin/env." AC_DEFINE(ENV_BINARY,"/usr/bin/env",[ Where is the command env located. ]) echo "/usr/bin/env" fi fi # Check if `xsltproc' is installed on your system echo -n "checking for xsltproc... " if test -x /usr/bin/xsltproc ; then XSLTPROC="/usr/bin/xsltproc" echo $XSLTPROC elif test -x /usr/local/bin/xsltproc; then XSLTPROC="/usr/local/bin/xsltproc" echo $XSLTPROC elif test -x /bin/xsltproc; then XSLTPROC="/bin/xsltproc" echo $XSLTPROC else XSLTPROC="/bin/true" echo "not found." fi AC_SUBST(XSLTPROC) # Check if `docbook-xsl' is installed on your system echo -n "checking for docbook-xsl... " if test -d /usr/share/xml/docbook/stylesheet/nwalsh ; then DBXSL="/usr/share/xml/docbook/stylesheet/nwalsh" echo $DBXSL else DBXSL= echo "not found." fi AC_SUBST(DBXSL) AC_CONFIG_FILES([ po/Makefile.in Makefile etc/Makefile lib/Makefile lib/hosts2xml man/Makefile schema/Makefile src/Makefile conf/Makefile conf/apt-dater.xml conf/hosts.xml etc/apt-dater.xml etc/hosts.xml build/apt-dater.spec ]) AC_OUTPUT apt-dater/include/0000775000175000017500000000000015131461765012161 5ustar memeapt-dater/include/adproto.h0000664000175000017500000000711615131461765014007 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2014 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _ADPROTO_H #define _ADPROTO_H #define _ADP_QUOTE(x) #x #define ADP_QUOTE(x) _ADP_QUOTE(x) /* Version history. */ #define ADP_VERSION_0_1 0.1 #define ADP_VERSION_0_2 0.2 #define ADP_VERSION_0_3 0.3 #define ADP_VERSION_0_4 0.4 #define ADP_VERSION_0_5 0.5 #define ADP_VERSION_0_6 0.6 #define ADP_VERSION_0_7 0.7 /* Most current protocol version. */ #define ADP_CURRENT_VERSION ADP_VERSION_0_7 /* Features since protocol version 0.1 */ #define ADP_FEATVER_ADPROTO ADP_VERSION_0_1 #define ADP_FEATVER_KERNELINFO ADP_VERSION_0_1 #define ADP_FEATVER_LSBREL ADP_VERSION_0_1 #define ADP_FEATVER_STATUS ADP_VERSION_0_1 /* Features since protocol version 0.2 */ #define ADP_FEATVER_FORBID ADP_VERSION_0_2 #define ADP_FEATVER_VIRT ADP_VERSION_0_2 /* Features since protocol version 0.3 */ #define ADP_FEATVER_UNAME ADP_VERSION_0_3 /* Features since protocol version 0.4 */ #define ADP_FEATVER_UUID ADP_VERSION_0_4 /* Features since protocol version 0.5 */ #define ADP_FEATVER_ADPERR ADP_VERSION_0_5 /* Features since protocol version 0.6 */ #define ADP_FEATVER_CLUSTER ADP_VERSION_0_6 /* Features since protocol version 0.6 */ #define ADP_FEATVER_NEEDRESTART ADP_VERSION_0_7 /* Patterns */ #define ADP_STRLEN_ADPERR 255 #define ADP_PATTERN_ADPERR "ADPERR: %"ADP_QUOTE(ADP_STRLEN_ADPERR)"s" #define ADP_PATTERN_ADPROTO "ADPROTO: %f" #define ADP_PATTERN_FORBID "FORBID: %d" #define ADP_STRLEN_KERNELINFO 255 #define ADP_PATTERN_KERNELINFO "KERNELINFO: %d %"ADP_QUOTE(ADP_STRLEN_KERNELINFO)"s" #define ADP_STRLEN_UNAME 255 #define ADP_PATTERN_UNAME "UNAME: %"ADP_QUOTE(ADP_STRLEN_UNAME)"s" #define ADP_STRLEN_UUID 36 #define ADP_PATTERN_UUID "UUID: %"ADP_QUOTE(ADP_STRLEN_UUID)"s" #define ADP_STRLEN_VIRT 255 #define ADP_PATTERN_VIRT "VIRT: %"ADP_QUOTE(ADP_STRLEN_VIRT)"s" #define ADP_STRLEN_CLUSTER 255 #define ADP_PATTERN_CLUSTER "CLUSTER: %"ADP_QUOTE(ADP_STRLEN_CLUSTER)"s" #define ADP_STRLEN_ADPERR 255 #define ADP_PATTERN_ADPERR "ADPERR: %"ADP_QUOTE(ADP_STRLEN_ADPERR)"s" #define ADP_STRLEN_NRVERSION 255 #define ADP_PATTERN_NRVERSION "NEEDRESTART-VER: %"ADP_QUOTE(ADP_STRLEN_NRVERSION)"s" #define ADP_STRLEN_NRKCURRENT 255 #define ADP_PATTERN_NRKCURRENT "NEEDRESTART-KCUR: %"ADP_QUOTE(ADP_STRLEN_NRKCURRENT)"s" #define ADP_STRLEN_NRKEXPECTED 255 #define ADP_PATTERN_NRKEXPECTED "NEEDRESTART-KEXP: %"ADP_QUOTE(ADP_STRLEN_NRKEXPECTED)"s" #define ADP_PATTERN_NRKSTATUS "NEEDRESTART-KSTA: %d" #define ADP_STATUS_NRK_UNKNOWN 0 #define ADP_STATUS_NRK_NOUPGR 1 #define ADP_STATUS_NRK_ABIUPGR 2 #define ADP_STATUS_NRK_VERUPGR 3 #define ADP_STRLEN_NRSERVICE 255 #define ADP_PATTERN_NRSERVICE "NEEDRESTART-SVC: %"ADP_QUOTE(ADP_STRLEN_NRSERVICE)"s" #endif /* _ADPROTO_H */ apt-dater/src/0000775000175000017500000000000015131461765011325 5ustar memeapt-dater/src/report.h0000664000175000017500000000222615131461765013013 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _REPORT_H #define _REPORT_H #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_XMLREPORT void initReport(GList *); gboolean ctrlReport (GList *); #endif #endif /* _REPORT_H */ apt-dater/src/colors.c0000664000175000017500000000751615131461765013003 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2012-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "colors.h" #include "ui.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif int uicolors[UI_COLOR_MAX]; #ifdef HAVE_COLOR static struct mapping_t Colors[] = { { "black", COLOR_BLACK }, { "blue", COLOR_BLUE }, { "cyan", COLOR_CYAN }, { "green", COLOR_GREEN }, { "magenta", COLOR_MAGENTA }, { "red", COLOR_RED }, { "white", COLOR_WHITE }, { "yellow", COLOR_YELLOW }, #ifdef HAVE_USE_DEFAULT_COLORS { "default", COLOR_DEFAULT }, #endif { NULL, 0 }, }; static struct mapping_t Components[] = { { "default", UI_COLOR_DEFAULT }, { "menu", UI_COLOR_MENU }, { "status", UI_COLOR_STATUS }, { "selector", UI_COLOR_SELECTOR }, { "hoststatus", UI_COLOR_HOSTSTATUS }, { "query", UI_COLOR_QUERY }, { "input", UI_COLOR_INPUT }, { NULL, 0 }, }; gboolean setColorForComponent(const gchar *component, const gchar *fg, const gchar *bg) { int i, pos = 0; short setfg, setbg, setcomp; gboolean isbright; setfg = setbg = setcomp = -1; if((isbright = g_str_has_prefix (fg, PREFIX_COLOR_BRIGHT)) == TRUE) pos = strlen(PREFIX_COLOR_BRIGHT); for(i = 0; Components[i].name; i++) { if(!g_ascii_strcasecmp(Components[i].name, component)) { setcomp = i; break; } } for(i = 0; Colors[i].name; i++) { if(!g_ascii_strcasecmp(Colors[i].name, &fg[pos])) { setfg = i; } if(!g_ascii_strcasecmp(Colors[i].name, bg)) { setbg = i; } } if(setfg == -1 || setbg == -1 || setcomp == -1) return(FALSE); init_pair(setcomp+1, Colors[setfg].value, Colors[setbg].value); uicolors[setcomp] = COLOR_PAIR(setcomp+1) | ((isbright == TRUE) ? A_BOLD : 0); return(TRUE); } gboolean setColors(const gchar **colors) { gint i, j; gchar **colordef; gboolean r = TRUE; if(has_colors() == FALSE) return(FALSE); i = 0; while(*(colors+i)) { if((colordef = g_strsplit(colors[i++], " ", 3))) { j = 0; while(*(colordef+j++)); if(j == 4) r&= setColorForComponent(colordef[0], colordef[1], colordef[2]); g_strfreev(colordef); } } return(r); } #endif /* HAVE_COLOR */ void ui_start_color () { short fg, bg; memset (uicolors, A_NORMAL, sizeof (gint) * UI_COLOR_MAX); /* Some defaults */ uicolors[UI_COLOR_DEFAULT] = A_NORMAL; uicolors[UI_COLOR_MENU] = A_REVERSE; uicolors[UI_COLOR_STATUS] = A_REVERSE; uicolors[UI_COLOR_SELECTOR] = A_REVERSE; uicolors[UI_COLOR_QUERY] = A_BOLD; uicolors[UI_COLOR_INPUT] = A_NORMAL; uicolors[UI_COLOR_HOSTSTATUS] = A_BOLD; #ifdef HAVE_COLOR start_color(); if(cfg->colors) if(setColors((const gchar **) cfg->colors) == FALSE) g_warning(_("Wrong color definition!")); #ifdef HAVE_USE_DEFAULT_COLORS if(pair_content(UI_COLOR_DEFAULT+1, &fg, &bg) != ERR) { if(!fg && !bg) fg = bg = COLOR_DEFAULT; assume_default_colors(fg, bg); } else use_default_colors(); #endif /* HAVE_USE_DEFAULT_COLORS */ #endif /* HAVE_COLOR */ } apt-dater/src/completion.h0000664000175000017500000000306415131461765013652 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * 2023 (C) Stefan Bühler * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _COMPLETION_H #define _COMPLETION_H #include /* Find entries in a GList that match a given prefix. * * Based on deprecated glib GCompletion */ typedef struct _completion { GList *entries; /* remember last prefix and the matched entries */ gchar* cached_prefix; GList* cached_entries; } Completion; Completion *completion_init(); /* entries: list of `DrawNode*` */ void completion_set_entries(Completion *cmpl, GList *entries); /* returns non-owned list; returned list gets invalidated with any other completion_* call on this instance */ GList *completion_search(Completion *cmpl, const gchar *prefix); void completion_free(Completion *cmpl); #endif /* _COMPLETION_H */ apt-dater/src/screen.h0000664000175000017500000000275715131461765012770 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SCREEN_H #define _SCREEN_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef FEAT_TMUX #include "apt-dater.h" #include "history.h" #ifndef __APPLE__ #define SCREEN_SDFORMT "%s/S-%s" #else #define SCREEN_SDFORMT "%s/.screen" #endif #define SCREEN_SOCKPRE "apt-dater_" gboolean screen_get_sessions(HostNode *n); gchar **screen_new(HostNode *n, const gboolean detached); gboolean screen_attach(HostNode *n, const SessNode *s, const gboolean shared); gchar *screen_get_dump(const SessNode *s); #endif /* !FEAT_TMUX */ #endif /* _SCREEN_H */ apt-dater/src/lock.c0000664000175000017500000000535015131461765012424 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "lock.h" #include "stats.h" #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif static GList *lockList = NULL; static gchar *getLockFile(const HostNode *n) { gchar *lockfile = NULL; lockfile = g_strdup_printf("%s.lck", n->statsfile); return(lockfile); } int setLockForHost(HostNode *n) { int r; gchar *lockfile = NULL; if(!(lockfile = getLockFile(n))) { g_error(_("Can't get the name of the lock file!")); return(EXIT_FAILURE); } if(n->fdlock < 0) { if((n->fdlock = open(lockfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP))< 0) { g_error("%s: %s", lockfile, strerror(errno)); g_free(lockfile); return(EXIT_FAILURE); } fcntl(n->fdlock, F_SETFD, FD_CLOEXEC | fcntl(n->fdlock, F_GETFD)); } #ifdef HAVE_FLOCK lockList = g_list_prepend(lockList, n); do { r = flock(n->fdlock, LOCK_EX | LOCK_NB); } while((r==-1) && (errno == EINTR)); if((r==-1) && (errno != EWOULDBLOCK)) { g_error(_("Failed to get lockfile %s: %s"), lockfile, g_strerror(errno)); } #else g_warning(_("Can't lock to file %s because function flock() is missing!"), lockfile); g_free(lockfile); return(EXIT_FAILURE); #endif g_free(lockfile); if(r == -1) { lockList = g_list_remove(lockList, n); close(n->fdlock); n->fdlock = -1; } return(r); } int unsetLockForHost(HostNode *n) { int r; if(n->fdlock < 0) { return(EXIT_FAILURE); } #ifdef HAVE_FLOCK r = flock(n->fdlock, LOCK_UN); lockList = g_list_remove(lockList, n); #else r = EXIT_SUCCESS; #endif close(n->fdlock); n->fdlock = -1; return(r); } static void cleanupLock(gpointer data, gpointer user_data) { unsetLockForHost((HostNode *)data); } void cleanupLocks() { g_list_foreach(lockList, cleanupLock, NULL); } apt-dater/src/env.h0000664000175000017500000000223715131461765012272 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2009-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _ENV_H #define _ENV_H #include "apt-dater.h" #include "history.h" void env_init(gchar **envp); gchar ** env_build(HostNode *n, const gchar *action, const gchar *param, const HistoryEntry *he); #endif /* _ENV_H */ apt-dater/src/apt-dater.h0000664000175000017500000001253215131461765013362 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _APT_DATER_H #define _APT_DATER_H #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "../include/adproto.h" #ifdef HAVE_GETTEXT #include #define _(x) gettext(x) #else #define _(x) x #endif #define N_(x) x #define STATS_MAX_LINE_LEN 1000 #define INPUT_MAX 4096 #define BUF_MAX_LEN 256 #define PROG_NAME PACKAGE #ifndef NDEBUG typedef enum { T_CFGFILE=1, T_DRAWNODE=2, T_SESSNODE=3, T_HOSTNODE=4, T_PKGNODE=5, T_MAPPING=6, T_VERSION=7, T_DISTRI=8, } etype; #define ASSERT_TYPE(p,t) \ if ((p)->_type != (t)) \ g_error("Unexpected type %d for " #p " in " __FILE__ ":%d" \ ", should be " #t "(%d)!\n", (p)->_type, __LINE__, (t)); #else #define ASSERT_TYPE(p,t) #endif typedef struct _cfgfile { #ifndef NDEBUG etype _type; #endif gchar *hostsfile; #ifdef FEAT_TMUX gchar *tmuxconffile; gchar *tmuxsockpath; #else gchar *screenrcfile; gchar *screentitle; #endif mode_t umask; gchar *statsdir; gchar *plugindir; gchar *ssh_cmd; gchar *sftp_cmd; gchar *ssh_optflags; gboolean ssh_agent; gchar **ssh_add; gsize ssh_numadd; gchar *cmd_refresh; gchar *cmd_upgrade; gchar *cmd_install; gboolean dump_screen; gboolean query_maintainer; gboolean beep; gboolean flash; #ifdef FEAT_HISTORY gboolean record_history; gchar *history_errpattern; gchar *history_dir; #endif gchar **colors; #ifdef FEAT_TCLFILTER gchar *filterexp; gchar *filterfile; #endif #ifdef FEAT_AUTOREF gboolean auto_refresh; #endif gchar *hook_pre_upgrade; gchar *hook_pre_refresh; gchar *hook_pre_install; gchar *hook_pre_connect; gchar *hook_post_upgrade; gchar *hook_post_refresh; gchar *hook_post_install; gchar *hook_post_connect; } CfgFile; typedef struct _update { #ifndef NDEBUG etype _type; #endif gchar *package; gchar *version; gint flag; gchar *data; } PkgNode; typedef struct _session { #ifndef NDEBUG etype _type; #endif gint pid; struct stat st; gboolean attached; } SessNode; typedef enum { C_UPDATES_PENDING = 0, C_UP_TO_DATE = 1, C_BROKEN_PKGS = 2, C_REFRESH_REQUIRED = 3, C_REFRESH = 4, C_SESSIONS = 5, #ifdef FEAT_TCLFILTER C_FILTERED = 6, C_UNKNOWN = 7, #else C_UNKNOWN = 6, #endif } Category; #define HOST_STATUS_PKGUPDATE 1 #define HOST_STATUS_PKGKEPTBACK 2 #define HOST_STATUS_PKGEXTRA 4 #define HOST_STATUS_PKGBROKEN 8 #define HOST_STATUS_KERNELUNKNOWN 16 #define HOST_STATUS_KERNELABIUPGR 32 #define HOST_STATUS_KERNELVERUPGR 64 #define HOST_STATUS_VIRTUALIZED 128 #define HOST_STATUS_LOCKED 256 #ifdef FEAT_CLUSTERS #define HOST_STATUS_CLUSTERED 512 #endif #define HOST_FORBID_REFRESH 1 #define HOST_FORBID_UPGRADE 2 #define HOST_FORBID_INSTALL 4 #define HOST_FORBID_MASK (HOST_FORBID_REFRESH | HOST_FORBID_UPGRADE | HOST_FORBID_INSTALL) #define _QUOTE(x) #x #define QUOTE(x) _QUOTE(x) #define UUID_STRLEN 36 typedef struct _hostnode { #ifndef NDEBUG etype _type; #endif gchar *hostname; gchar *comment; gchar *group; gchar *type; gchar *ssh_user; gchar *ssh_host; gint ssh_port; #ifdef FEAT_AUTOREF time_t last_upd; #endif guint status; gboolean keptback; #ifdef FEAT_TCLFILTER gboolean filtered; #endif #ifdef FEAT_HISTORY gboolean parse_result; gint hist_ts; #endif GFileMonitor *mon_ttymux; GFileMonitor *mon_stats; Category category; GList *packages; gint nupdates; gint nholds; gint nextras; gint nbrokens; GList *screens; gint fdlock; FILE *fpstat; gchar *statsfile; gchar *statstmpf; gchar *lsb_distributor; gchar *lsb_release; gchar *lsb_codename; gchar *uname_kernel; gchar *uname_machine; gchar *kernelrel; gchar *virt; gchar *adperr; gchar *identity_file; gint forbid; gboolean tagged; gchar uuid[UUID_STRLEN+1]; gint nrkstate; #ifdef FEAT_CLUSTERS GList *clusters; #endif } HostNode; #define HOST_SSHPORT_SET(n) \ ((n)->ssh_port != 0) struct mapping_t { gchar *name; gint value; }; #include "extern.h" #endif /* _APT_DATER_H */ apt-dater/src/main.c0000664000175000017500000001321115131461765012413 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include #include "apt-dater.h" #include "keyfiles.h" #include "ui.h" #include "stats.h" #include "sighandler.h" #include "lock.h" #include "env.h" #ifdef FEAT_XMLREPORT #include "report.h" #endif #ifndef SOURCE_DATE_UTC #error SOURCE_DATE_UTC is undefined! #endif #define VERSTEXT PACKAGE_STRING " - " SOURCE_DATE_UTC "\n\n" \ "Copyright Holder: IBH IT-Service GmbH [https://www.ibh.net/]\n\n" \ "This program is free software; you can redistribute it and/or modify\n" \ "it under the terms of the GNU General Public License as published by\n" \ "the Free Software Foundation; either version 2 of the License, or\n" \ "(at your option) any later version.\n\n" \ "Send bug reports to " PACKAGE_BUGREPORT ".\n\n" int main(int argc, char **argv, char **envp) { int opts; gchar *cfgfilename = NULL; gchar *cfgdirname = NULL; GList *hosts = NULL; #ifdef FEAT_XMLREPORT gboolean report = FALSE; gboolean refresh = TRUE; #endif #ifdef HAVE_GETTEXT setlocale(LC_ALL, ""); textdomain(PACKAGE); #endif /* initialize libxml2 & check for ABI mismatches */ LIBXML_TEST_VERSION #if !GLIB_CHECK_VERSION(2, 36, 0) /* since glib 2.36 this is done automatically */ g_type_init(); #endif cfgdirname = g_strdup_printf("%s/%s", g_get_user_config_dir(), PACKAGE); if(!cfgdirname) g_error(_("Out of memory.")); cfgfilename = g_strdup_printf("%s/apt-dater.xml", cfgdirname); if(!cfgfilename) g_error(_("Out of memory.")); g_set_prgname(PACKAGE); g_set_application_name(PACKAGE_STRING); if(chkForInitialConfig(cfgdirname, cfgfilename)) g_warning(_("Failed to create initial configuration file %s."), cfgfilename); while ((opts = getopt(argc, argv, "c:vrn")) != EOF) { switch(opts) { case 'c': if(cfgfilename) free(cfgfilename); cfgfilename = (char *) strdup(optarg); break; case 'v': g_print(VERSTEXT); exit(0); break; case 'r': #ifdef FEAT_XMLREPORT report = TRUE; #else g_error(_("Sorry, apt-dater was compiled w/o report feature!")); #endif break; #ifdef FEAT_XMLREPORT case 'n': refresh = FALSE; break; #endif default: #ifdef FEAT_XMLREPORT g_printerr(_("Usage: %s [-(c config|v|[n]r)]\n"), g_get_prgname()); #else g_printerr(_("Usage: %s [-(c config|v)]\n"), g_get_prgname()); #endif exit(EXIT_FAILURE); } } if(!cfgfilename) g_error(_("Out of memory.")); cfg = initialConfig(); if(!(loadConfig(cfgfilename, cfg))) { g_printerr(_("Error on loading config file %s\n"), cfgfilename); exit(EXIT_FAILURE); } if(!(hosts = (GList *) loadHosts(cfg->hostsfile))) { g_printerr(_("Error on loading config file %s\n"), cfg->hostsfile); exit(EXIT_FAILURE); } if(cfg->ssh_agent #ifdef FEAT_XMLREPORT && (!report || refresh) #endif ) { /* Spawn ssh-agent if needed */ if(getenv("SSH_AGENT_PID") == NULL) { gint i; gchar **agent_argv = g_new0(gchar *, argc+2); agent_argv[0] = "ssh-agent"; for(i=0; issh_numadd) { gint i; gchar **add_argv = g_new0(gchar *, cfg->ssh_numadd+2); add_argv[0] = PKGLIBDIR"/ssh-addonce"; for(i=0; issh_numadd; i++) add_argv[i+1] = cfg->ssh_add[i]; GError *error = NULL; if(g_spawn_sync(NULL, add_argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error) == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } g_free(add_argv); } } } env_init(envp); #ifdef FEAT_XMLREPORT if(!report) { #endif #ifndef FEAT_TMUX #ifdef __linux__ /* Test if we are the owner of the TTY or die. */ if(g_access("/proc/self/fd/0", R_OK|W_OK)) { g_error(_("Cannot open your terminal /proc/self/fd/0 - please check.")); exit(EXIT_FAILURE); } #endif #endif getOldestMtime(hosts); doUI(hosts); setSigHandler(); #ifdef FEAT_XMLREPORT } else { if(refresh) initReport(hosts); else initReport(NULL); } #endif loop = g_main_loop_new (NULL, FALSE); /* tmux supports GFileMonitor, screen requires polling */ #ifdef FEAT_TMUX refreshStats(hosts); #else g_timeout_add_seconds(1, (GSourceFunc) refreshStats, hosts); #endif #ifdef FEAT_XMLREPORT if(report) g_idle_add ((GSourceFunc) ctrlReport, hosts); else #endif g_idle_add ((GSourceFunc) ctrlUI, hosts); /* Startup the main loop */ g_main_loop_run (loop); g_main_loop_unref (loop); cleanUI(); cleanupLocks(); freeConfig(cfg); g_free(cfgfilename); g_free(cfgdirname); exit(EXIT_SUCCESS); } apt-dater/src/keyfiles.c0000664000175000017500000004061515131461765013312 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "keyfiles.h" #include "stats.h" #include "lock.h" #include "ttymux.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "../conf/apt-dater.xml.inc" #include "../conf/hosts.xml.inc" #ifdef FEAT_TMUX #include "../conf/tmux.conf.inc" #else #include "../conf/screenrc.inc" #endif void dump_config(const gchar *dir, const gchar *fn, const gchar *str, const unsigned int len) { gchar *pathtofile = g_strdup_printf("%s/%s", dir, (fn)); if(!pathtofile) g_error(_("Out of memory.")); if(g_file_test(pathtofile, G_FILE_TEST_IS_REGULAR|G_FILE_TEST_EXISTS) == FALSE) { FILE *fp = fopen(pathtofile, "wx"); g_message(_("Creating default config file %s"), pathtofile); if(fp) { if(fwrite(str, len, 1, fp) != 1) { g_printerr(_("Could not write to file %s."), pathtofile); exit(1); } fclose(fp); } } g_free(pathtofile); } int chkForInitialConfig(const gchar *cfgdir, const gchar *cfgfile) { if(g_file_test(cfgdir, G_FILE_TEST_IS_DIR) == FALSE) { if(g_mkdir_with_parents (cfgdir, S_IRWXU)) return(1); } dump_config(cfgdir, "apt-dater.xml", (gchar *)apt_dater_xml, apt_dater_xml_len); /* Convert legacy hosts.conf to new hosts.xml using external helper script. */ gchar *fnold = g_strdup_printf("%s/%s", cfgdir, "hosts.conf"); gchar *fnnew = g_strdup_printf("%s/%s", cfgdir, "hosts.xml"); if((g_file_test(fnnew, G_FILE_TEST_IS_REGULAR|G_FILE_TEST_EXISTS) == FALSE) && (g_file_test(fnold, G_FILE_TEST_IS_REGULAR|G_FILE_TEST_EXISTS) == TRUE)) { gchar *argv[3] = {PKGLIBDIR"/hosts2xml", "hosts2xml", NULL}; GError *error = NULL; if(g_spawn_sync(g_getenv ("HOME"), argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error) == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } } dump_config(cfgdir, "hosts.xml", (gchar *)hosts_xml, hosts_xml_len); #ifdef FEAT_TMUX dump_config(cfgdir, "tmux.conf", (gchar *)tmux_conf, tmux_conf_len); #else dump_config(cfgdir, "screenrc", (gchar *)screenrc, screenrc_len); #endif return(0); } void freeConfig (CfgFile *cfg) { g_free(cfg->hostsfile); g_free(cfg->statsdir); g_free(cfg->ssh_cmd); g_free(cfg->ssh_optflags); g_free(cfg->sftp_cmd); g_free(cfg->cmd_refresh); g_free(cfg->cmd_upgrade); g_free(cfg->cmd_install); #ifdef FEAT_TMUX g_free(cfg->tmuxconffile); g_free(cfg->tmuxsockpath); #else g_free(cfg->screenrcfile); g_free(cfg->screentitle); #endif g_strfreev(cfg->colors); g_free(cfg); } CfgFile *initialConfig() { CfgFile *lcfg = g_new0(CfgFile, 1); #ifndef NDEBUG lcfg->_type = T_CFGFILE; #endif lcfg->dump_screen = TRUE; #ifdef FEAT_AUTOREF lcfg->auto_refresh = TRUE; #endif lcfg->beep = TRUE; lcfg->flash = TRUE; #ifdef FEAT_HISTORY lcfg->record_history = TRUE; #endif lcfg->hook_pre_upgrade = "/etc/apt-dater/pre-upg.d"; lcfg->hook_pre_refresh = "/etc/apt-dater/pre-ref.d"; lcfg->hook_pre_install = "/etc/apt-dater/pre-ins.d"; lcfg->hook_pre_connect = "/etc/apt-dater/pre-con.d"; lcfg->hook_post_upgrade = "/etc/apt-dater/post-upg.d"; lcfg->hook_post_refresh = "/etc/apt-dater/post-ref.d"; lcfg->hook_post_install = "/etc/apt-dater/post-ins.d"; lcfg->hook_post_connect = "/etc/apt-dater/post-con.d"; lcfg->plugindir = "/etc/apt-dater/plugins"; return lcfg; } xmlXPathObjectPtr evalXPath(xmlXPathContextPtr context, const gchar *xpath) { xmlXPathObjectPtr result = xmlXPathEvalExpression(BAD_CAST(xpath), context); if(result == NULL) { g_warning("xmlXPathEvalExpression '%s' failed!\n", xpath); exit(1); } return result; } char *getXPropStr(const xmlNodePtr nodes[], const gchar *attr, const gchar *defval) { int i; xmlChar *val = NULL; for(i = 0; nodes[i]; i++) { val = xmlGetProp(nodes[i], BAD_CAST(attr)); if(val) { gchar *ret = g_strdup((gchar *)val); xmlFree(val); return ret; } } if(defval) return g_strdup(defval); return NULL; } int getXPropInt(const xmlNodePtr nodes[], const gchar *attr, const int defval) { gchar *sval = getXPropStr(nodes, attr, NULL); if(!sval) return defval; int ival = strtol(sval, NULL, 0); g_free(sval); return ival; } int getXPropBool(const xmlNodePtr nodes[], const gchar *attr, const gboolean defval) { gchar *val = getXPropStr(nodes, attr, NULL); if(!val) return defval; g_strstrip(val); if(g_ascii_strncasecmp(val, "false", 1) == 0 || g_ascii_strncasecmp(val, "no", 1) == 0) { g_free(val); return FALSE; } if(g_ascii_strncasecmp(val, "true", 1) == 0 || g_ascii_strncasecmp(val, "yes", 1) == 0) { g_free(val); return TRUE; } if(atoi(val)) { g_free(val); return TRUE; } g_free(val); return FALSE; } xmlNodeSetPtr getXNodes(xmlXPathContextPtr context, const gchar *xpath) { xmlXPathObjectPtr xobj = xmlXPathEvalExpression(BAD_CAST(xpath), context); xmlNodeSetPtr ret = xobj->nodesetval; xmlXPathFreeNodeSetList(xobj); return ret; } xmlNodePtr getXNode(xmlXPathContextPtr context, const gchar *xpath) { xmlNodeSetPtr set = getXNodes(context, xpath); xmlNodePtr ret = NULL; if(!xmlXPathNodeSetIsEmpty(set)) ret = set->nodeTab[0]; xmlXPathFreeNodeSet(set); return ret; } void xmlErrIgnoreHandler(void *ctx, const char *msg, ...) { } void handleXMLError(const xmlErrorPtr e) { if(!e) return; if(e->domain == XML_FROM_IO && e->code == XML_IO_LOAD_ERROR && e->level == XML_ERR_WARNING) return; switch(e->level) { case XML_ERR_WARNING: fprintf(stderr, "WARNING - "); break; case XML_ERR_ERROR: fprintf(stderr, "ERROR - "); break; case XML_ERR_FATAL: fprintf(stderr, "FATAL - "); break; default: break; } if(e->file && e->line) { fprintf(stderr, "%s:%d: ", e->file, e->line); } else { if(e->file) { fprintf(stderr, "%s: ", e->file); } } fprintf(stderr, "%s", e->message); xmlResetError(e); } gboolean loadConfig(const gchar *filename, CfgFile *lcfg) { /* Parse hosts.xml document. */ xmlDocPtr xcfg = xmlParseFile(filename); if(xcfg == NULL) return(FALSE); /* Handle Xincludes. */ xmlSetGenericErrorFunc(NULL, xmlErrIgnoreHandler); xmlXIncludeProcess(xcfg); handleXMLError( xmlGetLastError() ); xmlSetGenericErrorFunc(NULL, NULL); /* Validate against DTD. */ xmlValidCtxtPtr xval = xmlNewValidCtxt(); if(xmlValidateDocument(xval, xcfg) == 0) { xmlFreeValidCtxt(xval); return(FALSE); } xmlFreeValidCtxt(xval); /* Allocate XPath context. */ xmlXPathContextPtr xctx = xmlXPathNewContext(xcfg); if(!xctx) { g_error("%s: xmlXPathNewContext failed!\n", filename); return(FALSE); } xmlNodePtr s_ssh[2] = {getXNode(xctx, "/apt-dater/ssh"), NULL}; xmlNodePtr s_path[2] = {getXNode(xctx, "/apt-dater/paths"), NULL}; #ifdef FEAT_TMUX xmlNodePtr s_tmux[2] = {getXNode(xctx, "/apt-dater/tmux"), NULL}; #else xmlNodePtr s_screen[2] = {getXNode(xctx, "/apt-dater/screen"), NULL}; #endif xmlNodePtr s_appearance[2] = {getXNode(xctx, "/apt-dater/appearance"), NULL}; xmlNodePtr s_notify[2] = {getXNode(xctx, "/apt-dater/notify"), NULL}; xmlNodePtr s_hooks[2] = {getXNode(xctx, "/apt-dater/hooks"), NULL}; #ifdef FEAT_AUTOREF xmlNodePtr s_autoref[2] = {getXNode(xctx, "/apt-dater/auto-ref"), NULL}; #endif #ifdef FEAT_HISTORY xmlNodePtr s_history[2] = {getXNode(xctx, "/apt-dater/history"), NULL}; #endif #ifdef FEAT_TCLFILTER xmlNodePtr s_tclfilter[2] = {getXNode(xctx, "/apt-dater/tcl-filter"), NULL}; #endif lcfg->ssh_optflags = getXPropStr(s_ssh, "opt-cmd-flags", "-t"); lcfg->ssh_cmd = getXPropStr(s_ssh, "cmd", "/usr/bin/ssh"); lcfg->sftp_cmd = getXPropStr(s_ssh, "sftp-cmd", "/usr/bin/sftp"); lcfg->umask = getXPropInt(s_path, "umask", S_IRWXG | S_IRWXO); umask(lcfg->umask); lcfg->hostsfile = getXPropStr(s_path, "hosts-file", g_strdup_printf("%s/%s/%s", g_get_user_config_dir(), PROG_NAME, "hosts.xml")); lcfg->statsdir = getXPropStr(s_path, "stats-dir", g_strdup_printf("%s/%s/%s", g_get_user_cache_dir(), PROG_NAME, "stats")); if(g_mkdir_with_parents(lcfg->statsdir, S_IRWXU | S_IRWXG) == -1) { g_warning("Failed to create %s: %s", lcfg->statsdir, g_strerror(errno)); exit(1); } #ifdef FEAT_TMUX lcfg->tmuxconffile = getXPropStr(s_tmux, "conf-file", g_strdup_printf("%s/%s/%s", g_get_user_config_dir(), PROG_NAME, "tmux.conf")); lcfg->tmuxsockpath = getXPropStr(s_tmux, "socket-path", g_strdup_printf("%s/%s/%s", g_get_user_cache_dir(), PROG_NAME, "tmux")); if(g_mkdir_with_parents(lcfg->tmuxsockpath, S_IRWXU | S_IRWXG) == -1) { g_warning("Failed to create %s: %s", lcfg->tmuxsockpath, g_strerror(errno)); exit(1); } #else lcfg->screenrcfile = getXPropStr(s_screen, "rc-file", g_strdup_printf("%s/%s/%s", g_get_user_config_dir(), PROG_NAME, "screenrc")); lcfg->screentitle = getXPropStr(s_screen, "title", g_strdup("%m # %U%H")); #endif lcfg->ssh_agent = getXPropBool(s_ssh, "spawn-agent", FALSE); xmlNodeSetPtr s_addkeys = getXNodes(xctx, "/apt-dater/ssh/add-key"); if(!xmlXPathNodeSetIsEmpty(s_addkeys)) { lcfg->ssh_add = g_new0(char*, s_addkeys->nodeNr + 1); int i; for(i = 0; i < s_addkeys->nodeNr; i++) { lcfg->ssh_add[i] = g_strdup((gchar *)xmlGetProp(s_addkeys->nodeTab[i], BAD_CAST("name"))); xmlChar *c = xmlGetProp(s_addkeys->nodeTab[i], BAD_CAST("fn")); if(!c) { g_printerr(_("Empty SSH key filename (%s/@fn) in configuration."), xmlGetNodePath(s_addkeys->nodeTab[i])); exit(1); } lcfg->ssh_add[i] = g_strdup((gchar *)c); } lcfg->ssh_numadd = s_addkeys->nodeNr; } xmlXPathFreeNodeSet(s_addkeys); #ifdef FEAT_TMUX // XXX needs to be ported to tmux XXX lcfg->dump_screen = FALSE; lcfg->query_maintainer = FALSE; // XXX needs to be ported to tmux XXX #else lcfg->dump_screen = !getXPropBool(s_screen, "no-dumps", FALSE); lcfg->query_maintainer = getXPropBool(s_screen, "query-maintainer", FALSE); #endif gchar *colors = getXPropStr(s_appearance, "colors", "menu brightgreen blue;status brightgreen blue;selector black red;"); if(colors) lcfg->colors = g_strsplit(colors, ";", -1); #ifdef FEAT_TCLFILTER lcfg->filterexp = getXPropStr(s_tclfilter, "filter-exp", NULL); lcfg->filterfile = getXPropStr(s_tclfilter, "filter-file", NULL); #endif #ifdef FEAT_AUTOREF lcfg->auto_refresh = getXPropBool(s_autoref, "enabled", TRUE); #endif lcfg->beep = getXPropBool(s_notify, "beep", TRUE); lcfg->flash = getXPropBool(s_notify, "flash", TRUE); #ifdef FEAT_HISTORY lcfg->record_history = getXPropBool(s_history, "record", TRUE); lcfg->history_errpattern = getXPropStr(s_history, "err-pattern", "((?history_dir = getXPropStr(s_path, "history-dir", g_strdup_printf("%s/%s/history", g_get_user_data_dir(), PACKAGE)); #endif lcfg->hook_pre_upgrade = getXPropStr(s_hooks, "pre-upgrade", "/etc/apt-dater/pre-upg.d"); lcfg->hook_pre_refresh = getXPropStr(s_hooks, "pre-refresh", "/etc/apt-dater/pre-ref.d"); lcfg->hook_pre_install = getXPropStr(s_hooks, "pre-install", "/etc/apt-dater/pre-ins.d"); lcfg->hook_pre_connect = getXPropStr(s_hooks, "pre-connect", "/etc/apt-dater/pre-con.d"); lcfg->hook_post_upgrade = getXPropStr(s_hooks, "post-upgrade", "/etc/apt-dater/post-upg.d"); lcfg->hook_post_refresh = getXPropStr(s_hooks, "post-refresh", "/etc/apt-dater/post-ref.d"); lcfg->hook_post_install = getXPropStr(s_hooks, "post-install", "/etc/apt-dater/post-ins.d"); lcfg->hook_post_connect = getXPropStr(s_hooks, "post-connect", "/etc/apt-dater/post-con.d"); lcfg->plugindir = getXPropStr(s_hooks, "plugin-dir", "/etc/apt-dater/plugins"); return (TRUE); } gint cmp_hosts(gconstpointer a, gconstpointer b, gpointer p) { HostNode *ha = (HostNode *)a; HostNode *hb = (HostNode *)b; gint ret; ret = g_strcmp0(ha->group, hb->group); if(ret == 0) ret = g_strcmp0(ha->hostname, hb->hostname); return ret; } GList *loadHosts (const gchar *filename) { /* Parse hosts.xml document. */ xmlDocPtr xcfg = xmlParseFile(filename); if(xcfg == NULL) return(FALSE); /* Handle Xincludes. */ xmlSetGenericErrorFunc(NULL, xmlErrIgnoreHandler); xmlXIncludeProcess(xcfg); handleXMLError( xmlGetLastError() ); xmlSetGenericErrorFunc(NULL, NULL); /* Validate against DTD. */ xmlValidCtxtPtr xval = xmlNewValidCtxt(); if(xmlValidateDocument(xval, xcfg) == 0) { xmlFreeValidCtxt(xval); return(FALSE); } xmlFreeValidCtxt(xval); /* Allocate XPath context. */ xmlXPathContextPtr xctx = xmlXPathNewContext(xcfg); if(!xctx) { g_error("%s: xmlXPathNewContext failed!\n", filename); return(FALSE); } /* Lookup global host template node. */ xmlXPathObjectPtr defaults = evalXPath(xctx, "/hosts/default"); xmlNodePtr defhost = NULL; if(!xmlXPathNodeSetIsEmpty(defaults->nodesetval)) defhost = defaults->nodesetval->nodeTab[0]; xmlXPathFreeObject(defaults); /* Iterate over /hosts/group nodes. */ xmlXPathObjectPtr groups = evalXPath(xctx, "/hosts/group"); int i; GList *hostlist = NULL; for(i = 0; i < groups->nodesetval->nodeNr; i++) { xmlNodePtr group = groups->nodesetval->nodeTab[i]; xmlChar *groupname = xmlGetProp(group, BAD_CAST("name")); if(!groupname) { g_printerr("%s: The group element #%d does not have a name attribute!\n", filename, i+1); return(FALSE); } xctx->node = group; xmlXPathObjectPtr hosts = evalXPath(xctx, "host"); if(xmlXPathNodeSetIsEmpty(hosts->nodesetval)) { xmlXPathFreeObject(hosts); xmlFree(groupname); g_warning("%s: The group '%s' is empty!\n", filename, groupname); continue; } /* Iterate over /hosts/group/host nodes. */ int j; for(j = 0; j < hosts->nodesetval->nodeNr; j++) { xmlNodePtr host = hosts->nodesetval->nodeTab[j]; xmlNodePtr cfgnodes[4] = {host, group, defhost, NULL}; xmlChar *hostname = xmlGetProp(host, BAD_CAST("name")); if(!hostname) { g_printerr("%s: The host element #%d of group '%s' does not have a name attribute!\n", filename, j+1, groupname); xmlXPathFreeObject(hosts); xmlXPathFreeObject(groups); xmlFree(groupname); return(FALSE); } HostNode *hostnode = g_new0(HostNode, 1); #ifndef NDEBUG hostnode->_type = T_HOSTNODE; #endif hostnode->hostname = g_strdup((gchar *)hostname); hostnode->comment = getXPropStr(cfgnodes, "comment", NULL); hostnode->type = getXPropStr(cfgnodes, "type", "generic-ssh"); hostnode->ssh_user = getXPropStr(cfgnodes, "ssh-user", NULL); hostnode->ssh_host = getXPropStr(cfgnodes, "ssh-host", NULL); hostnode->ssh_port = getXPropInt(cfgnodes, "ssh-port", 0); hostnode->identity_file = getXPropStr(cfgnodes, "ssh-id", NULL); hostnode->group = g_strdup((gchar *)groupname); hostnode->statsfile = g_strdup_printf("%s/%s:%d.stat", cfg->statsdir, hostnode->hostname, hostnode->ssh_port); hostnode->statstmpf = g_strdup_printf("%s/%s:%d.stat.new", cfg->statsdir, hostnode->hostname, hostnode->ssh_port); hostnode->fdlock = -1; hostnode->uuid[0] = 0; hostnode->tagged = FALSE; getUpdatesFromStat(hostnode); TTYMUX_INITIALIZE(hostnode); stats_initialize(hostnode); hostlist = g_list_prepend(hostlist, hostnode); xmlFree(hostname); } xmlXPathFreeObject(hosts); xmlFree(groupname); } xmlXPathFreeObject(groups); return g_list_sort_with_data(hostlist, cmp_hosts, NULL); } apt-dater/src/sighandler.h0000664000175000017500000000211715131461765013617 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SIGHANDLER_H #define _SIGHANDLER_H #include void setSigHandler(); void ignoreSIGINT(gboolean); #endif /* SIGHANDLER_H */ apt-dater/src/Makefile.am0000664000175000017500000000302615131461765013362 0ustar memebin_PROGRAMS = apt-dater adsh # https://reproducible-builds.org/docs/source-date-epoch/#makefile SOURCE_DATE_EPOCH ?= $(shell date +%s) SOURCE_DATE_UTC ?= $(shell LC_ALL=C date -u -d "@$(SOURCE_DATE_EPOCH)" 2>/dev/null || LC_ALL=C date -u -r "$(SOURCE_DATE_EPOCH)" 2>/dev/null || LC_ALL=C date -u) if FEAT_RUNCUST FEAT_RUNCUST_SOURCES=runcust.h runcust.c else FEAT_RUNCUST_SOURCES= endif if FEAT_TMUX FEAT_TTYMUX_SOURCES=tmux.c tmux.h else FEAT_TTYMUX_SOURCES=screen.c screen.h endif SHARED_SOURCES = \ ../include/adproto.h \ apt-dater.c \ apt-dater.h \ colors.c \ colors.h \ completion.c \ completion.h \ exec.c \ exec.h \ extern.h \ keyfiles.c \ keyfiles.h \ lock.c \ lock.h \ parsecmd.c \ parsecmd.h \ report.c \ report.h \ sighandler.c \ sighandler.h \ stats.c \ stats.h \ ui.c \ ui.h \ autoref.c \ autoref.h \ tag.c \ tag.h \ history.c \ history.h \ env.c \ env.h \ clusters.c \ clusters.h \ glue.c \ glue.h \ ttymux.h \ $(FEAT_RUNCUST_SOURCES) $(FEAT_TTYMUX_SOURCES) apt_dater_SOURCES = \ main.c \ $(SHARED_SOURCES) apt_dater_LDADD = $(GLIB_LIBS) $(TCL_LIBS) $(LIBXML2_LIBS) $(GIO_LIBS) adsh_SOURCES = \ adsh.c \ $(SHARED_SOURCES) adsh_LDADD = $(apt_dater_LDADD) AM_CFLAGS = -Wall -DPKGLIBDIR='"'$(pkglibdir)'"' -DSOURCE_DATE_UTC="\"$(SOURCE_DATE_UTC)\"" if LINUX AM_LDFLAGS = -Wl,--as-needed else AM_LDFLAGS = -Wl endif AM_CPPFLAGS = $(GLIB_CFLAGS) $(TCL_CFLAGS) $(LIBXML2_CFLAGS) $(GIO_CFLAGS) apt-dater/src/runcust.c0000664000175000017500000000270015131461765013173 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2017 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_NCURSES_H #include #else #include #endif #ifdef FEAT_RUNCUST #include "apt-dater.h" #include "colors.h" #include "runcust.h" void runCustom() { WINDOW *w = newwin(LINES-3, COLS, 2, 0); wattron(w, uicolors[UI_COLOR_QUERY]); mvwaddstr(w, 1, 0, _("Select command to run:")); wattroff(w, uicolors[UI_COLOR_QUERY]); waddstr(w, "\n"); wattroff(w, uicolors[UI_COLOR_INPUT]); } #endif /* FEAT_RUNCUST */ apt-dater/src/env.c0000664000175000017500000001216015131461765012261 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2009-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "env.h" #include "ui.h" GSList *base_env = NULL; void env_init(gchar **envp) { g_assert(base_env == NULL); int i; for(i=0; envp[i]; i++) { base_env = g_slist_prepend(base_env, envp[i]); } #define ADD_GENV(name, value) \ base_env = g_slist_prepend(base_env, g_strdup_printf("AD_"name"=%s", ((value) ? (value) : ""))) ADD_GENV("HOSTSFILE" , cfg->hostsfile); #ifdef FEAT_TMUX ADD_GENV("TMUXSOCKPATH" , cfg->tmuxsockpath); #else ADD_GENV("SCREENRCFILE" , cfg->screenrcfile); #endif ADD_GENV("STATSDIR" , cfg->statsdir); ADD_GENV("SSH_CMD" , cfg->ssh_cmd); ADD_GENV("SFTP_CMD" , cfg->sftp_cmd); ADD_GENV("SSH_OPTFLAGS" , cfg->ssh_optflags); #ifdef FEAT_HISTORY ADD_GENV("HIST_RECORD" , cfg->record_history ? "true" : "false"); ADD_GENV("HIST_ERRPATTERN" , cfg->history_errpattern); #else ADD_GENV("HIST_RECORD" , "false"); #endif ADD_GENV("HOOK_PRE_UPGRADE" , cfg->hook_pre_upgrade); ADD_GENV("HOOK_PRE_REFRESH" , cfg->hook_pre_refresh); ADD_GENV("HOOK_PRE_INSTALL" , cfg->hook_pre_install); ADD_GENV("HOOK_PRE_CONNECT" , cfg->hook_pre_connect); ADD_GENV("HOOK_POST_UPGRADE" , cfg->hook_post_upgrade); ADD_GENV("HOOK_POST_REFRESH", cfg->hook_post_refresh); ADD_GENV("HOOK_POST_INSTALL", cfg->hook_post_install); ADD_GENV("HOOK_POST_CONNECT", cfg->hook_post_connect); ADD_GENV("PLUGINDIR", cfg->plugindir); } gchar ** env_build(HostNode *n, const gchar *action, const gchar *param, const HistoryEntry *he) { GPtrArray *new_env = g_ptr_array_sized_new(g_slist_length(base_env) + 25 #ifdef FEAT_CLUSTERS + g_list_length(n->clusters) #endif ); GSList *p; for(p = base_env; p; p = p->next) g_ptr_array_add(new_env, g_strdup(p->data)); #define ADD_HENV(name, value) \ g_ptr_array_add(new_env, g_strdup_printf("AD_" name "=%s", value)) ADD_HENV("HOSTNAME" , n->hostname); if(n->comment) ADD_HENV("COMMENT" , n->comment); else ADD_HENV("COMMENT" , ""); ADD_HENV("GROUP" , n->group); if(n->ssh_user) ADD_HENV("SSH_USER" , n->ssh_user); else ADD_HENV("SSH_USER" , ""); ADD_HENV("SSH_HOST" , (n->ssh_host ? n->ssh_host : n->hostname)); if(n->ssh_port) g_ptr_array_add(new_env, g_strdup_printf("AD_SSH_PORT=%d", n->ssh_port)); else ADD_HENV("SSH_PORT" , ""); if(n->identity_file && strlen(n->identity_file) > 0) g_ptr_array_add(new_env, g_strdup_printf("AD_SSH_ID=-i %s", n->identity_file)); else ADD_HENV("SSH_ID" , ""); ADD_HENV("STATSFILE" , n->statsfile); ADD_HENV("KERNEL" , n->kernelrel); ADD_HENV("LSB_DISTRI" , n->lsb_distributor); ADD_HENV("LSB_RELEASE" , n->lsb_release); ADD_HENV("LSB_CODENAME" , n->lsb_codename); ADD_HENV("UNAME_KERNEL" , n->uname_kernel); ADD_HENV("UNAME_MACHINE" , n->uname_machine); ADD_HENV("VIRT" , n->virt); ADD_HENV("TYPE" , n->type); ADD_HENV("UUID" , n->uuid); #ifdef FEAT_HISTORY if(cfg->record_history && he) { gchar *hp = history_rec_path(cfg, n); gchar *fn_meta = g_strdup_printf("%s/meta", hp); ADD_HENV("HIST_PATH" , hp); g_free(hp); history_write_meta(fn_meta, he); g_free(fn_meta); } else #endif ADD_HENV("HIST_PATH" , ""); #ifdef FEAT_CLUSTERS if(n->clusters != NULL) { g_ptr_array_add(new_env, g_strdup_printf("AD_CLUSTERS=%d", g_list_length(n->clusters))); int j = 1; GList *c = n->clusters; while(c != NULL) { g_ptr_array_add(new_env, g_strdup_printf("AD_CLUSTER%d=%s", j, (gchar *)c->data)); c = c->next; j++; } } else #endif ADD_HENV("CLUSTERS" , "0"); ADD_HENV("ACTION" , action); if(param) ADD_HENV("PARAM" , param); else ADD_HENV("PARAM" , ""); ADD_HENV("MAINTAINER", maintainer); /* add legacy MAINTAINER env variable */ g_ptr_array_add(new_env, g_strdup_printf("MAINTAINER=%s", maintainer)); g_ptr_array_add(new_env, NULL); return (gchar **) g_ptr_array_free(new_env, FALSE); } apt-dater/src/exec.c0000664000175000017500000001461715131461765012426 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "ttymux.h" #include "exec.h" #include "stats.h" #include "parsecmd.h" #include "history.h" #include "env.h" #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif gboolean ssh_cmd_refresh(HostNode *n) { gboolean r; GError *error = NULL; gchar *cmd = NULL; gchar *argv[2] = {PKGLIBDIR"/cmd", NULL}; gchar *output = NULL; /* gchar *identity_file = NULL; */ GPid child_pid; gint standard_output; GIOChannel *iocstdout; g_assert(n); prepareStatsFile(n); gchar **env = env_build(n, "refresh", NULL, NULL); r = g_spawn_async_with_pipes(g_getenv ("HOME"), /* working_directory */ argv, env, /* envp */ G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, /* GSpawnFlags */ NULL, /* GSpawnChildSetupFunc */ NULL, /* user_data */ &child_pid, NULL, /* standard_input */ &standard_output, /* &standard_output */ NULL, /* standard_error */ &error); g_strfreev(env); if(r == TRUE) { iocstdout = g_io_channel_unix_new (standard_output); g_io_channel_set_flags(iocstdout, G_IO_FLAG_NONBLOCK, &error); g_io_channel_set_buffer_size (iocstdout, 8192); g_io_add_watch_full (iocstdout, G_PRIORITY_DEFAULT, G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN, setStatsFileFromIOC, n, refreshStatsOfNode); } if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } g_free(output); g_free(cmd); return r; } gboolean ssh_cmd_upgrade(HostNode *n, const gboolean detached) { gboolean r; guint i; GError *error = NULL; gchar **argv = NULL; g_assert(n); if(n->forbid & HOST_FORBID_UPGRADE) return FALSE; HistoryEntry he; he.ts = time(NULL); he.maintainer = maintainer; he.action = "upgrade"; he.data = NULL; gchar **screen_argv = TTYMUX_NEW(n, detached); argv = (gchar **) g_malloc0(sizeof(gchar *) * (g_strv_length(screen_argv) + 2)); for(i = 0; i < g_strv_length(screen_argv); i++) argv[i] = g_strdup(screen_argv[i]); argv[i] = g_strdup(PKGLIBDIR"/cmd"); g_strfreev(screen_argv); #ifdef FEAT_HISTORY n->parse_result = cfg->history_errpattern && strlen(cfg->history_errpattern);; #endif gchar **env = env_build(n, "upgrade", NULL, &he); r = g_spawn_sync(g_getenv ("HOME"), argv, env, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); g_strfreev(env); g_strfreev(argv); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } #ifdef FEAT_HISTORY if(!detached && n->parse_result && !TTYMUX_GET_SESSIONS(n)) { n->parse_result = FALSE; return history_ts_failed(cfg, n); } #endif return FALSE; } gboolean ssh_cmd_install(HostNode *n, gchar *package, const gboolean detached) { gboolean r; guint i; GError *error = NULL; gchar **argv = NULL; g_assert(n); if(n->forbid & HOST_FORBID_INSTALL) return FALSE; HistoryEntry he; he.ts = time(NULL); he.maintainer = maintainer; he.action = "install"; he.data = package; gchar **screen_argv = TTYMUX_NEW(n, detached); argv = (gchar **) g_malloc0(sizeof(gchar *) * (g_strv_length(screen_argv) + 2)); for(i = 0; i < g_strv_length(screen_argv); i++) argv[i] = g_strdup(screen_argv[i]); argv[i] = g_strdup(PKGLIBDIR"/cmd"); g_strfreev(screen_argv); #ifdef FEAT_HISTORY n->parse_result = cfg->history_errpattern && strlen(cfg->history_errpattern); #endif gchar **env = env_build(n, "install", package, &he); r = g_spawn_sync(g_getenv ("HOME"), argv, env, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); g_strfreev(env); g_strfreev(argv); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } #ifdef FEAT_HISTORY if(!detached && n->parse_result && !TTYMUX_GET_SESSIONS(n)) { n->parse_result = FALSE; return history_ts_failed(cfg, n); } #endif return FALSE; } void ssh_connect(HostNode *n, const gboolean detached) { gboolean r; guint i; GError *error = NULL; gchar **argv; HistoryEntry he; he.ts = time(NULL); he.maintainer = maintainer; he.action = "connect"; he.data = NULL; gchar **screen_argv = TTYMUX_NEW(n, detached); argv = (gchar **) g_malloc0(sizeof(gchar *) * (g_strv_length(screen_argv) + 2)); for(i = 0; i < g_strv_length(screen_argv); i++) argv[i] = g_strdup(screen_argv[i]); argv[i] = g_strdup(PKGLIBDIR"/cmd"); g_strfreev(screen_argv); gchar **env = env_build(n, "connect", NULL, &he); r = g_spawn_sync(g_getenv("HOME"), argv, env, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); g_strfreev(env); g_strfreev(argv); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } } void sftp_connect(HostNode *n) { gboolean r; guint i; GError *error = NULL; gchar **argv; g_assert(n); HistoryEntry he; he.ts = time(NULL); he.maintainer = maintainer; he.action = "transfer"; he.data = NULL; gchar **screen_argv = TTYMUX_NEW(n, FALSE); argv = (gchar **) g_malloc0(sizeof(gchar *) * (g_strv_length(screen_argv) + 2)); for(i = 0; i < g_strv_length(screen_argv); i++) argv[i] = g_strdup(screen_argv[i]); argv[i] = g_strdup(PKGLIBDIR"/cmd"); g_strfreev(screen_argv); gchar **env = env_build(n, "transfer", NULL, &he); r = g_spawn_sync(g_getenv("HOME"), argv, env, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); g_strfreev(env); g_strfreev(argv); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } } apt-dater/src/ttymux.h0000664000175000017500000000346415131461765013057 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _TTYMUX_H #define _TTYMUX_H #include "apt-dater.h" #include "history.h" #ifdef FEAT_TMUX #include "tmux.h" #define TTYMUX_INITIALIZE(n) tmux_initialize((n)) #define TTYMUX_GET_SESSIONS(n) tmux_get_sessions((n)) #define TTYMUX_NEW(n,detached) tmux_new((n), (detached)) #define TTYMUX_ATTACH(n, s, shared) tmux_attach((n), (s), (shared)) #define TTYMUX_GET_DUMP(s) tmux_get_dump((s)) #define TTYMUX_IS_ATTACHED(s) ((s)->attached) #else #include "screen.h" #define TTYMUX_INITIALIZE(n) #define TTYMUX_GET_SESSIONS(n) screen_get_sessions((n)) #define TTYMUX_NEW(n,detached) screen_new((n), (detached)) #define TTYMUX_ATTACH(n, s, shared) screen_attach((n), (s), (shared)) #define TTYMUX_GET_DUMP(s) screen_get_dump((s)) #define TTYMUX_IS_ATTACHED(s) ((s)->attached) #endif #endif /* _TTYMUX_H */ apt-dater/src/stats.h0000664000175000017500000000267015131461765012641 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _STATS_H #define _STATS_H #include #include "apt-dater.h" void getOldestMtime(GList *hosts); void freeUpdates(GList *updates); void stats_initialize(HostNode *); gboolean refreshStats(GList *); gboolean setStatsFileFromIOC(GIOChannel *, GIOCondition, gpointer); gchar *getStatsFileName(const HostNode *); gboolean prepareStatsFile(HostNode *); gboolean getUpdatesFromStat(HostNode *); void refreshStatsOfNode(gpointer); gchar *getStatsContent(const HostNode *); #endif /* _STATS_H */ apt-dater/src/autoref.h0000664000175000017500000000230115131461765013137 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2012-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _AUTOREF_H #define _AUTOREF_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_AUTOREF void autoref_add_host_info(HostNode *node); void autoref_rem_host_info(HostNode *node); guint autoref_trigger_auto(); #endif #endif /* _AUTOREF_H */ apt-dater/src/completion.c0000664000175000017500000000634015131461765013645 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * 2023 (C) Stefan Bühler * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "completion.h" #include "ui.h" Completion *completion_init() { Completion *cmpl = NULL; cmpl = g_new0(Completion, 1); return cmpl; } static void completion_reset(Completion *cmpl) { g_list_free(cmpl->entries); cmpl->entries = NULL; g_free(cmpl->cached_prefix); cmpl->cached_prefix = NULL; g_list_free(cmpl->cached_entries); cmpl->cached_entries = NULL; } void completion_set_entries(Completion *cmpl, GList *entries) { completion_reset(cmpl); cmpl->entries = g_list_copy(entries); } static gboolean prefix_match(const gchar *entry_name, const gchar *prefix, gsize prefix_len) { return 0 == g_ascii_strncasecmp(entry_name, prefix, prefix_len); } static gboolean entry_match(GList *entry, const gchar *prefix, gsize prefix_len) { gchar *entry_name; entry_name = getStrFromDrawNode((DrawNode *) entry->data); return prefix_match(entry_name, prefix, prefix_len); } GList *completion_search(Completion *cmpl, const gchar *prefix) { GList *entry = NULL; gsize prefix_len; prefix_len = strlen(prefix); if (0 == prefix_len) { return cmpl->entries; } if (cmpl->cached_prefix) { gsize cached_len = strlen(cmpl->cached_prefix); if (cached_len <= prefix_len && prefix_match(prefix, cmpl->cached_prefix, cached_len)) { if (cached_len == prefix_len) { return cmpl->cached_entries; } for (entry = cmpl->cached_entries; entry; ) { GList *next = g_list_next(entry); if (!entry_match(entry, prefix, prefix_len)) { cmpl->cached_entries = g_list_delete_link(cmpl->cached_entries, entry); } entry = next; } goto search_done; } } /* no cache / cache prefix mismatch: */ g_list_free(cmpl->cached_entries); cmpl->cached_entries = NULL; for (entry = cmpl->entries; entry; entry = g_list_next(entry)) { if (entry_match(entry, prefix, prefix_len)) { cmpl->cached_entries = g_list_prepend(cmpl->cached_entries, entry->data); } } search_done: /* Modified cache; remember prefix (unless result is empty)*/ if (cmpl->cached_prefix) { g_free(cmpl->cached_prefix); cmpl->cached_prefix = NULL; } if (cmpl->cached_entries) { cmpl->cached_prefix = g_strdup(prefix); } return cmpl->cached_entries; } void completion_free(Completion *cmpl) { if (!cmpl) return; completion_reset(cmpl); g_free(cmpl); } apt-dater/src/parsecmd.h0000664000175000017500000000225515131461765013300 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PARSECMD_H #define _PARSECMD_H #include "apt-dater.h" gchar *parse_string(const gchar *src, const HostNode *n); gboolean parse_cmdline(const char *s, int *argcPtr, char *** argvPtr, const HostNode *n); #endif /* _PARSECMD_H */ apt-dater/src/clusters.c0000664000175000017500000000337015131461765013340 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2012-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "clusters.h" #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef FEAT_CLUSTERS static gint findCluster(gconstpointer a, gconstpointer b) { return g_ascii_strcasecmp(a, b); } void cluster_host_add(HostNode *n, const gchar *c) { if(g_list_find_custom(n->clusters, c, findCluster) == NULL) { n->clusters = g_list_insert_sorted(n->clusters, g_strdup(c), findCluster); n->status = n->status | HOST_STATUS_CLUSTERED; } } static void freeCluster(gchar *data, gpointer *user_data) { if(data) g_free(data); } void cluster_host_reset(HostNode *n) { if(n && n->clusters) { g_list_foreach(n->clusters, (GFunc) freeCluster, NULL); g_list_free(n->clusters); n->clusters = NULL; } } #endif /* FEAT_CLUSTERS */ apt-dater/src/keyfiles.h0000664000175000017500000000232215131461765013310 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _KEYFILES_H #define _KEYFILES_H CfgFile *initialConfig(); GList *loadHosts (const char *filename); gboolean loadConfig (const char *filename, CfgFile *); void freeConfig (CfgFile *cfg); int chkForInitialConfig(const gchar *, const gchar *); #endif /* _KEYFILES_H */ apt-dater/src/clusters.h0000664000175000017500000000224215131461765013342 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2012-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CLUSTERS_H #define _CLUSTERS_H #include "apt-dater.h" #ifdef FEAT_CLUSTERS void cluster_host_add(HostNode *, const gchar *); void cluster_host_reset(HostNode *); #endif /* FEAT_CLUSTERS */ #endif /* _CLUSTERS_H */ apt-dater/src/lock.h0000664000175000017500000000211415131461765012424 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LOCK_H #define _LOCK_H int setLockForHost(HostNode *); int unsetLockForHost(HostNode *); void cleanupLocks(); #endif /* _LOCK_H */ apt-dater/src/tag.c0000664000175000017500000001065215131461765012250 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "tag.h" #include "ui.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #include typedef enum { COMPCMD_ALL, COMPCMD_CODENAME, COMPCMD_DISTRIBUTOR, COMPCMD_GROUP, COMPCMD_PACKAGE, COMPCMD_UPDATE, COMPCMD_HOSTNAME, COMPCMD_FLAG, #ifdef FEAT_CLUSTERS COMPCMD_CLUSTERS, #endif } COMPCMD; struct ValidCompCmds { gchar c; gchar *name; COMPCMD cmd; }; static struct ValidCompCmds compCmds[] = { {'A', "all", COMPCMD_ALL}, {'d', "distributor", COMPCMD_DISTRIBUTOR}, {'c', "codename", COMPCMD_CODENAME}, {'g', "group", COMPCMD_GROUP}, {'h', "hostname", COMPCMD_HOSTNAME}, {'p', "package", COMPCMD_PACKAGE}, {'u', "update" , COMPCMD_UPDATE}, {'f', "flag" , COMPCMD_FLAG}, #ifdef FEAT_CLUSTERS {'C', "cluster" , COMPCMD_CLUSTERS}, #endif { 0, NULL, 0}, }; static gboolean compStrWithPattern(const gchar *str, gchar *pattern, gsize s) { gboolean r = FALSE; if(!str || !pattern) return FALSE; #if (GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 14) GRegex *regex = g_regex_new (pattern, G_REGEX_CASELESS, 0, NULL); if(regex) { r = g_regex_match (regex, str, 0, NULL); g_regex_unref (regex); } #else gsize maxsize; gint i; maxsize = s > strlen(pattern) ? strlen(pattern) : s; for(i=0; i= maxsize;i++) if(!g_ascii_strncasecmp (&str[i], pattern, maxsize)) { r = TRUE; break; } #endif return r; } gboolean compHostWithPattern(HostNode *n, gchar *in, gsize s) { gboolean r = FALSE; gchar *pattern = NULL; gint i, j, compflags = 0; COMPCMD compcmd = COMPCMD_HOSTNAME; if(!in || !n || strlen(in) < 1) return FALSE; if(g_str_has_prefix(in, "~") == TRUE && strlen(in) >= 2) { /* Check if is a valid compare command identifier. */ for(i=0; compCmds[i].name;i++) { if(compCmds[i].c == in[1]) { compcmd = compCmds[i].cmd; break; } } if(!compCmds[i].name) return FALSE; /* No valid compare command! */ else pattern = g_strdup(&in[2]); } else pattern = g_strdup(in); if(!pattern) return FALSE; g_strchug(pattern); switch(compcmd) { case COMPCMD_UPDATE: case COMPCMD_PACKAGE: { GList *p = g_list_first(n->packages); while(p) { PkgNode *pn = p->data; if((compcmd == COMPCMD_UPDATE ? pn->flag & HOST_STATUS_PKGUPDATE : TRUE) && compStrWithPattern(pn->package, pattern, s) == TRUE) { r = TRUE; break; } p = g_list_next(p); } } break; /* case COMPCMD_UPDATE */ case COMPCMD_ALL: r = TRUE; break; case COMPCMD_DISTRIBUTOR: r = n->lsb_distributor ? compStrWithPattern(n->lsb_distributor, pattern, s) : FALSE; break; case COMPCMD_CODENAME: r = n->lsb_codename ? compStrWithPattern(n->lsb_codename, pattern, s) : FALSE; break; case COMPCMD_GROUP: r = compStrWithPattern(n->group, pattern, s); break; case COMPCMD_FLAG: compflags=0; for(i = 0; i < (strlen(pattern) > s ? s : strlen(pattern)); i++) { j=0; while(hostFlags[j].code) { if(hostFlags[j].code[0] == pattern[i]) compflags |= hostFlags[j].flag; j++; } } if(n->status - (n->status ^ compflags) == compflags) r = TRUE; break; #ifdef FEAT_CLUSTERS case COMPCMD_CLUSTERS: { GList *c = n->clusters; while(c != NULL) { if(compStrWithPattern(c->data, pattern, s) == TRUE) { r = TRUE; break; } c = c->next; } } break; #endif case COMPCMD_HOSTNAME: default: r = compStrWithPattern(n->hostname, pattern, s); } /* switch */ g_free(pattern); return r; } apt-dater/src/history.h0000664000175000017500000000436515131461765013207 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _HISTORY_H #define _HISTORY_H #include "apt-dater.h" typedef struct _historyEntry { time_t ts; gint duration; gchar *path; gchar *maintainer; gchar *action; gchar *data; gchar *errpattern; } HistoryEntry; #ifdef FEAT_HISTORY static inline gchar *history_path(const CfgFile *cfg, const HostNode *n) { return g_strdup_printf("%s/%s:%d", cfg->history_dir, n->hostname, n->ssh_port); } static inline gchar *history_ts_path(const CfgFile *cfg, const HostNode *n) { return g_strdup_printf("%s/%s:%d/%d-%d", cfg->history_dir, n->hostname, n->ssh_port, n->hist_ts, getpid()); } static inline gchar *history_rec_path(const CfgFile *cfg, HostNode *n) { n->hist_ts = time(NULL); gchar *p = history_ts_path(cfg, n); g_mkdir_with_parents(p, S_IRWXU | S_IRWXG); return p; } GList *history_get_entries(const CfgFile *, const HostNode *); HistoryEntry *history_recent_entry(const CfgFile *, const HostNode *); void history_write_meta(const gchar *, const HistoryEntry *); void history_free_he(HistoryEntry *); void history_free_hel(GList *); void history_show_less(HistoryEntry *); void history_show_replay(HistoryEntry *); void history_show_less_search(HistoryEntry *, gchar *pattern); gboolean history_ts_failed(const CfgFile *, HostNode *); #endif #endif /* _HISTORY_H */ apt-dater/src/report.c0000664000175000017500000002057415131461765013014 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "ui.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_XMLREPORT #include #include #include static xmlTextWriterPtr writer; static xmlDocPtr doc; #ifdef FEAT_HISTORY #include "history.h" #endif void initReport(GList *hosts) { /* * this initialize the library and check potential ABI mismatches * between the version it was compiled for and the actual shared * library used. */ LIBXML_TEST_VERSION writer = xmlNewTextWriterDoc(&doc, 0); if (writer == NULL) g_error(_("Error creating the xml output.")); xmlTextWriterStartDocument(writer, NULL, NULL, NULL); xmlTextWriterWriteDTD(writer, BAD_CAST("report"), NULL, BAD_CAST(XML_SCHEMA_URI"/report.dtd"), NULL); if(!hosts) return; fprintf(stderr, _("apt-dater is refreshing %d hosts, please standby...\n"), g_list_length(hosts)); GList *n = hosts; while(n) { ((HostNode *)n->data)->category = C_REFRESH_REQUIRED; n = g_list_next(n); } } #ifdef FEAT_HISTORY static void reportHistory(gpointer data, gpointer user_data) { HistoryEntry *h = (HistoryEntry *)data; xmlTextWriterStartElement(writer, BAD_CAST(h->action)); xmlTextWriterWriteFormatElement(writer, BAD_CAST("timestamp"), "%d", (int)h->ts); xmlTextWriterWriteFormatElement(writer, BAD_CAST("duration"), "%d", h->duration); xmlTextWriterStartElement(writer, BAD_CAST("path")); xmlTextWriterWriteString(writer, BAD_CAST(h->path)); xmlTextWriterEndElement(writer); if(h->maintainer && strlen(h->maintainer)) { xmlTextWriterStartElement(writer, BAD_CAST("maintainer")); xmlTextWriterWriteString(writer, BAD_CAST(h->maintainer)); xmlTextWriterEndElement(writer); } xmlTextWriterEndElement(writer); } #endif #ifdef FEAT_CLUSTERS static void reportCluster(gpointer data, gpointer user_data) { xmlTextWriterWriteFormatElement(writer, BAD_CAST("member-of"), "%s", (gchar *)data); } #endif static void reportPackage(gpointer data, gpointer user_data) { PkgNode *n = (PkgNode *)data; xmlTextWriterStartElement(writer, BAD_CAST("pkg")); xmlTextWriterWriteAttribute(writer, BAD_CAST("name"), BAD_CAST(n->package)); xmlTextWriterWriteAttribute(writer, BAD_CAST("version"), BAD_CAST(n->version)); if(n->flag & HOST_STATUS_PKGUPDATE) xmlTextWriterWriteAttribute(writer, BAD_CAST("hasupdate"), BAD_CAST("1")); if(n->flag & HOST_STATUS_PKGKEPTBACK) xmlTextWriterWriteAttribute(writer, BAD_CAST("onhold"), BAD_CAST("1")); if(n->flag & HOST_STATUS_PKGEXTRA) xmlTextWriterWriteAttribute(writer, BAD_CAST("extra"), BAD_CAST("1")); if(n->flag & HOST_STATUS_PKGBROKEN) xmlTextWriterWriteAttribute(writer, BAD_CAST("broken"), BAD_CAST("1")); if(n->data) xmlTextWriterWriteAttribute(writer, BAD_CAST("data"), BAD_CAST(n->data)); xmlTextWriterEndElement(writer); } static void reportHost(gpointer data, gpointer lgroup) { HostNode *n = (HostNode *)data; if(*(char **)lgroup == NULL || strcmp(*(char **)lgroup, n->group)) { if(*(char **)lgroup) xmlTextWriterEndElement(writer); xmlTextWriterStartElement(writer, BAD_CAST("group")); xmlTextWriterWriteAttribute(writer, BAD_CAST("name"), BAD_CAST(n->group)); *(char **)lgroup = n->group; } /* Begin host element. */ xmlTextWriterStartElement(writer, BAD_CAST("host")); xmlTextWriterWriteAttribute(writer, BAD_CAST("hostname"), BAD_CAST(n->hostname)); if(n->comment) xmlTextWriterWriteAttribute(writer, BAD_CAST("comment"), BAD_CAST(n->comment)); #ifdef FEAT_TCLFILTER if(n->filtered) xmlTextWriterWriteAttribute(writer, BAD_CAST("filtered"), BAD_CAST("1")); #endif /* Status */ xmlTextWriterStartElement(writer, BAD_CAST("status")); xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("status"), "%d", n->category); xmlTextWriterWriteString(writer, BAD_CAST(drawCategories[n->category])); xmlTextWriterEndElement(writer); /* SSH config */ xmlTextWriterStartElement(writer, BAD_CAST("ssh")); if(n->ssh_user) xmlTextWriterWriteElement(writer, BAD_CAST("user"), BAD_CAST(n->ssh_user)); if(n->ssh_host) xmlTextWriterWriteFormatElement(writer, BAD_CAST("host"), "%s", n->ssh_host); if(n->ssh_port) xmlTextWriterWriteFormatElement(writer, BAD_CAST("port"), "%d", n->ssh_port); xmlTextWriterEndElement(writer); /* Kernel info */ xmlTextWriterStartElement(writer, BAD_CAST("kernel")); if(n->status & (HOST_STATUS_KERNELABIUPGR | HOST_STATUS_KERNELVERUPGR)) xmlTextWriterWriteAttribute(writer, BAD_CAST("reboot"), BAD_CAST("1")); if(n->kernelrel) xmlTextWriterWriteString(writer, BAD_CAST(n->kernelrel)); xmlTextWriterEndElement(writer); /* LSB info */ xmlTextWriterStartElement(writer, BAD_CAST("lsb")); if(n->lsb_distributor) xmlTextWriterWriteElement(writer, BAD_CAST("distri"), BAD_CAST(n->lsb_distributor)); if(n->lsb_release) xmlTextWriterWriteElement(writer, BAD_CAST("release"), BAD_CAST(n->lsb_release)); if(n->lsb_codename) xmlTextWriterWriteElement(writer, BAD_CAST("codename"), BAD_CAST(n->lsb_codename)); xmlTextWriterEndElement(writer); /* UNAME info */ xmlTextWriterStartElement(writer, BAD_CAST("uname")); if(n->uname_kernel) xmlTextWriterWriteElement(writer, BAD_CAST("kernel"), BAD_CAST(n->uname_kernel)); if(n->uname_machine) xmlTextWriterWriteElement(writer, BAD_CAST("machine"), BAD_CAST(n->uname_machine)); xmlTextWriterEndElement(writer); /* virtualization info */ if(n->virt) xmlTextWriterWriteElement(writer, BAD_CAST("virt"), BAD_CAST(n->virt)); else xmlTextWriterWriteElement(writer, BAD_CAST("virt"), BAD_CAST("Unknown")); /* host UUID */ if(n->uuid[0]) xmlTextWriterWriteElement(writer, BAD_CAST("uuid"), BAD_CAST(n->uuid)); #ifdef FEAT_HISTORY /* history data */ GList *hel = history_get_entries(cfg, n); if(hel) { xmlTextWriterStartElement(writer, BAD_CAST("history")); g_list_foreach(hel, reportHistory, NULL); xmlTextWriterEndElement(writer); history_free_hel(hel); } #endif #ifdef FEAT_CLUSTERS /* clusters data */ if(n->clusters) { xmlTextWriterStartElement(writer, BAD_CAST("clusters")); g_list_foreach(n->clusters, reportCluster, NULL); xmlTextWriterEndElement(writer); } #endif /* Packages */ xmlTextWriterStartElement(writer, BAD_CAST("packages")); g_list_foreach(n->packages, reportPackage, NULL); xmlTextWriterEndElement(writer); xmlTextWriterEndElement(writer); } gboolean ctrlReport(GList *hosts) { gint torefresh = 0; char *lgroup = NULL; g_usleep(G_USEC_PER_SEC); GList *n = hosts; while(n) { if( (((HostNode *)n->data)->category == C_REFRESH_REQUIRED) || (((HostNode *)n->data)->category == C_REFRESH) ) torefresh++; n = g_list_next(n); } if(torefresh == 0) { /* Create root node. */ xmlTextWriterStartElement(writer, BAD_CAST("report")); xmlTextWriterWriteFormatElement(writer, BAD_CAST("timestamp"), "%d", (int)time(NULL)); /* Put node stats to file. */ g_list_foreach(hosts, reportHost, &lgroup); /* Close open host element. */ if(lgroup) xmlTextWriterEndElement(writer); /* Close root node and finalize document. */ xmlTextWriterEndElement(writer); xmlTextWriterEndDocument(writer); xmlFreeTextWriter(writer); xmlSaveCtxtPtr save = xmlSaveToFd(fileno(stdout), NULL, XML_SAVE_FORMAT); xmlSaveDoc(save, doc); xmlSaveClose(save); xmlFreeDoc(doc); g_main_loop_quit (loop); fprintf(stderr, "\ndone\n"); } return torefresh > 0; } #endif apt-dater/src/runcust.h0000664000175000017500000000221215131461765013176 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2017 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _RUNCUST_H #define _RUNCUST_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_RUNCUST #include "apt-dater.h" void runCustom(); #endif /* FEAT_RUNCUST */ #endif /* _RUNCUST_H */ apt-dater/src/glue.c0000664000175000017500000000514615131461765012433 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "glue.h" #if !GLIB_CHECK_VERSION(2, 14, 0) #warning Your glib2 does not support g_timeout_seconds (glib >= 2.14.0) - using glue code. guint g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data) { g_timeout_add(interval*1000, function, data); } #endif #if !GLIB_CHECK_VERSION(2, 34, 0) #warning Your glib2 does not support g_spawn_check_exit_status (glib >= 2.34.0) - using glue code. /* Implementation has been taken from: * * gspawn.[ch] - Process launching * * Copyright 2000 Red Hat, Inc. * g_execvpe implementation based on GNU libc execvp: * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc. */ #include #include gboolean g_spawn_check_exit_status(gint exit_status, GError **error) { gboolean ret = FALSE; if (WIFEXITED (exit_status)) { if (WEXITSTATUS (exit_status) != 0) { g_set_error (error, G_SPAWN_ERROR, WEXITSTATUS (exit_status), _("Child process exited with code %ld"), (long) WEXITSTATUS (exit_status)); goto out; } } else if (WIFSIGNALED (exit_status)) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Child process killed by signal %ld"), (long) WTERMSIG (exit_status)); goto out; } else if (WIFSTOPPED (exit_status)) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Child process stopped by signal %ld"), (long) WSTOPSIG (exit_status)); goto out; } else { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Child process exited abnormally")); goto out; } ret = TRUE; out: return ret; } #endif apt-dater/src/tmux.c0000664000175000017500000001345115131461765012472 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_TMUX #include "tmux.h" #include "parsecmd.h" #include "history.h" #include "stats.h" static struct passwd *pw = NULL; const static gchar * tmux_get_sdir() { static gchar sdir[256]; if (!pw) pw = getpwuid(getuid()); if (!pw) return NULL; g_snprintf(sdir, sizeof(sdir), TMUX_SDFORMT, TMUX_SOCKPATH, pw->pw_name); return sdir; } void tmux_changed(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { HostNode *n = user_data; g_assert(n); GList *nl = g_list_append(NULL, n); refreshStats(nl); nl = g_list_remove(nl, n); } void tmux_initialize(HostNode *n) { gchar *sp = g_strdup_printf("%s/%s_%s_%d", cfg->tmuxsockpath, n->ssh_user, n->hostname, n->ssh_port); GFile *path = g_file_new_for_path(sp); g_free(sp); n->mon_ttymux = g_file_monitor(path, G_FILE_MONITOR_SEND_MOVED, NULL, NULL); g_object_unref(path); g_signal_connect(n->mon_ttymux, "changed", G_CALLBACK(tmux_changed), n); } gboolean tmux_get_sessions(HostNode *n) { g_assert(n); if (n->screens) { g_list_free(n->screens); n->screens = NULL; } const gchar *sdir = tmux_get_sdir(); if (!sdir) return FALSE; gchar *sock = g_strdup_printf("%s/%s_%s_%d", cfg->tmuxsockpath, n->ssh_user, n->hostname, n->ssh_port); gchar *argv[7] = {TMUX_BINARY, "-S", sock, "list-session", "-F", "#{session_name}\t#{session_created}\t#{session_attached}", NULL}; gchar *out = NULL; gint rc; GError *error = NULL; gboolean r = g_spawn_sync(NULL, /* working_directory */ argv, NULL, /* envp */ G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, /* GSpawnFlags */ NULL, /* GSpawnChildSetupFunc */ NULL, /* user_data */ &out, /* &standard_output */ NULL, /* standard_error */ &rc, /* return code */ &error); g_free(sock); if(r == FALSE) { g_warning("failed to run tmux: %s", error->message); g_clear_error (&error); return FALSE; } #if GLIB_CHECK_VERSION(2, 70, 0) if(!g_spawn_check_wait_status(rc, &error)) { #else /* deprecated name for g_spawn_check_wait_status: */ if(!g_spawn_check_exit_status(rc, &error)) { #endif /* g_warning("error on list-sessions: %s", error->message); g_clear_error (&error);*/ return FALSE; } gchar **lines = g_strsplit(out, "\n", 0xff); gint i = -1; while(lines[++i] && strlen(lines[i])) { SessNode *s = g_new0(SessNode, 1); #ifndef NDEBUG s->_type = T_SESSNODE; #endif gchar **line = g_strsplit(lines[i], "\t", 4); gint j = -1; while(line[++j] && j < 3) { if(j == 0) { sscanf(line[j], "%d", &(s->pid)); continue; } if(j == 1) { s->st.st_mtime = strtoull(line[j], NULL, 10); continue; } if(j == 2) { gint h; sscanf(line[j], "%d", &h); s->attached = (h != 0); continue; } } g_strfreev(line); n->screens = g_list_append(n->screens, s); } g_strfreev(lines); return g_list_length(n->screens) > 0; } gchar ** tmux_new(HostNode *n, const gboolean detached) { gchar **_argv = (gchar **) g_malloc0(sizeof(gchar *) * 9); gchar *title = parse_string("%m # %U%H", n); _argv[0] = g_strdup(TMUX_BINARY); _argv[1] = g_strdup("-S"); _argv[2] = g_strdup_printf("%s/%s_%s_%d", cfg->tmuxsockpath, n->ssh_user, n->hostname, n->ssh_port); _argv[3] = g_strdup("-f"); _argv[4] = g_strdup(cfg->tmuxconffile); _argv[5] = g_strdup("new-session"); _argv[6] = g_strdup_printf("-%sn", detached ? "d" : ""); _argv[7] = title; _argv[8] = NULL; return _argv; } static gchar ** tmux_attach_cmd(const HostNode *n, const SessNode *s, const gboolean shared) { gchar **_argv = (gchar **) g_malloc0(sizeof(gchar *) * 7); _argv[0] = g_strdup(TMUX_BINARY); _argv[1] = g_strdup("-S"); _argv[2] = g_strdup_printf("%s/%s_%s_%d", cfg->tmuxsockpath, n->ssh_user, n->hostname, n->ssh_port); _argv[3] = g_strdup("attach-session"); _argv[4] = g_strdup("-t"); _argv[5] = g_strdup_printf("%d", s->pid); _argv[6] = NULL; return _argv; } gboolean tmux_attach(HostNode *n, const SessNode *s, const gboolean shared) { gboolean r; GError *error = NULL; gchar **argv = tmux_attach_cmd(n, s, shared); g_assert(n); r = g_spawn_sync(g_getenv ("HOME"), argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } g_strfreev(argv); #ifdef FEAT_HISTORY if(n->parse_result && !tmux_get_sessions(n)) { n->parse_result = FALSE; return history_ts_failed(cfg, n); } #endif return FALSE; } gchar * tmux_get_dump(const SessNode *s) { return NULL; } #endif /* FEAT_TMUX */ apt-dater/src/colors.h0000664000175000017500000000237615131461765013007 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2012-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _COLOR_H #define _COLOR_H #define COLOR_DEFAULT -1 #define PREFIX_COLOR_BRIGHT "bright" enum { UI_COLOR_DEFAULT = 0, UI_COLOR_MENU, UI_COLOR_STATUS, UI_COLOR_SELECTOR, UI_COLOR_BODY, UI_COLOR_QUERY, UI_COLOR_INPUT, UI_COLOR_HOSTSTATUS, UI_COLOR_MAX, }; void ui_start_color(); #endif /* _COLOR_H */ apt-dater/src/tmux.h0000664000175000017500000000257415131461765012503 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _TMUX_H #define _TMUX_H #include "apt-dater.h" #include "history.h" #define TMUX_SDFORMT "%s/S-%s" #define TMUX_SOCKPRE "apt-dater_" #define TMUX_SOCKPATH "/tmp" void tmux_initialize(HostNode *n); gboolean tmux_get_sessions(HostNode *n); gchar **tmux_new(HostNode *n, const gboolean detached); gboolean tmux_attach(HostNode *n, const SessNode *s, const gboolean shared); gchar *tmux_get_dump(const SessNode *s); #endif /* _TMUX_H */ apt-dater/src/autoref.c0000664000175000017500000002677015131461765013152 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_AUTOREF /** * We record installed/updatable packages on the hosts. When a host has been * refreshed, we look at each package and put it in the following tree (this * will be used later to detect if some hosts are outdated running the same * distri): * * [ht_distris] * | * +--[Debian/4.0/etch] * | +--[package1] * | | +--(version1)->(host1, host2, host3) * | | +--(version2)->(host5, host6, ...) * | | | * | | ... * | +--[package2] * | | * | ... * +--[CentOS/4.7/Final] * | * ... * * [ ]: GHashTable * ( ): GList * **/ #include typedef struct _distri { #ifndef NDEBUG etype _type; #endif gchar *lsb_distributor; gchar *lsb_release; gchar *lsb_codename; gchar *uname_kernel; gchar *uname_machine; } Distri; typedef struct _version { #ifndef NDEBUG etype _type; #endif gchar *version; time_t ts_first; GList *nodes; } Version; static GHashTable *ht_distris = NULL; /* ====================[ begin: stuff taken from libdpkg.a ]==================== */ struct versionrevision { unsigned long epoch; const char *version; const char *revision; }; static inline int cisdigit(int c) { return (c>='0') && (c<='9'); } static int cisalpha(int c) { return ((c>='a') && (c<='z')) || ((c>='A') && (c<='Z')); } /* assume ascii; warning: evaluates x multiple times! */ #define order(x) ((x) == '~' ? -1 \ : cisdigit((x)) ? 0 \ : !(x) ? 0 \ : cisalpha((x)) ? (x) \ : (x) + 256) static int verrevcmp(const char *val, const char *ref) { if (!val) val= ""; if (!ref) ref= ""; while (*val || *ref) { int first_diff= 0; while ( (*val && !cisdigit(*val)) || (*ref && !cisdigit(*ref)) ) { int vc= order(*val), rc= order(*ref); if (vc != rc) return vc - rc; val++; ref++; } while ( *val == '0' ) val++; while ( *ref == '0' ) ref++; while (cisdigit(*val) && cisdigit(*ref)) { if (!first_diff) first_diff= *val - *ref; val++; ref++; } if (cisdigit(*val)) return 1; if (cisdigit(*ref)) return -1; if (first_diff) return first_diff; } return 0; } /* =====================[ end: stuff taken from libdpkg.a ]===================== */ /* Creates a string from LSB fields used for the hashtable. */ static inline gchar *distri2str(const Distri *d, gchar *buf, const gint bsize) { snprintf(buf, bsize-1, "%s|%s|%s|%s|%s", d->lsb_distributor, d->lsb_release, d->lsb_codename, d->uname_kernel, d->uname_machine); return buf; } /* Compares to distris. */ static gboolean distri_equal(gconstpointer a, gconstpointer b) { gchar da[0x1ff]; gchar db[0x1ff]; return g_str_equal(distri2str(a, da, sizeof(da)), distri2str(b, db, sizeof(db))); } /* Hash a distri. */ static guint distri_hash(gconstpointer key) { Distri *distri = (Distri *)key; gchar b[0x1ff]; ASSERT_TYPE(distri, T_DISTRI); return g_str_hash(distri2str(distri, b, sizeof(b))); } static gint cmp_vers(gconstpointer a, gconstpointer b) { ASSERT_TYPE((Version *)a, T_VERSION); ASSERT_TYPE((Version *)b, T_VERSION); return -verrevcmp(((Version *)a)->version, ((Version *)b)->version); } static inline int test_pkg(const PkgNode *pkg) { return (pkg->flag & (HOST_STATUS_PKGEXTRA | HOST_STATUS_PKGBROKEN)) == 0; } /* Add PkgNode to package hashtable */ static void add_pkg(gpointer data, gpointer user_data) { PkgNode *pkg = (PkgNode *)data; GHashTable *ht_packages = (GHashTable *)(((gpointer *)user_data)[0]); HostNode *node = (HostNode *)(((gpointer *)user_data)[1]); ASSERT_TYPE(pkg, T_PKGNODE); ASSERT_TYPE(node, T_HOSTNODE); if (!test_pkg(pkg)) return; gchar *v; if(((pkg->flag & HOST_STATUS_PKGUPDATE) || (pkg->flag & HOST_STATUS_PKGKEPTBACK)) && (pkg->data)) v = pkg->data; else v = pkg->version; if(!v) return; /* Create packages hashtable if needed. */ GList *l_versions = g_hash_table_lookup(ht_packages, pkg->package); Version _v; #ifndef NDEBUG _v._type = T_VERSION; #endif _v.version = v; /* Create version entry if needed. */ Version *vers = NULL; GList *l_v = g_list_find_custom(l_versions, &_v, cmp_vers); if (l_v) { vers = (Version *)l_v->data; ASSERT_TYPE(vers, T_VERSION); } if(!vers) { vers = g_malloc(sizeof(Version)); #ifndef NDEBUG vers->_type = T_VERSION; #endif vers->version = strdup(v); vers->ts_first = node->last_upd; vers->nodes = NULL; l_versions = g_list_prepend(l_versions, vers); g_hash_table_insert(ht_packages, g_strdup(pkg->package), l_versions); } else if (vers->ts_first > node->last_upd) { vers->ts_first = node->last_upd; } vers->nodes = g_list_prepend(vers->nodes, node); } /* Test if host should be observed by autoref. */ static inline int test_hostnode(const HostNode *node) { return (node->lsb_distributor && node->lsb_release && node->lsb_codename && node->uname_kernel && node->uname_machine); } /* Fetch package data from HostNode and feed it into the hashtables. */ void autoref_add_host_info(HostNode *node) { Distri distri; if(!test_hostnode(node)) return; if (!ht_distris) ht_distris = g_hash_table_new(distri_hash, distri_equal); #ifdef NDEBUG #define ASSIGN_DIST(d, n, f) \ (d).lsb_distributor = f((n)->lsb_distributor); \ (d).lsb_release = f((n)->lsb_release); \ (d).lsb_codename = f((n)->lsb_codename); \ (d).uname_kernel = f((n)->uname_kernel); \ (d).uname_machine = f((n)->uname_machine); #else #define ASSIGN_DIST(d, n, f) \ (d)._type = T_DISTRI; \ (d).lsb_distributor = f((n)->lsb_distributor); \ (d).lsb_release = f((n)->lsb_release); \ (d).lsb_codename = f((n)->lsb_codename); \ (d).uname_kernel = f((n)->uname_kernel); \ (d).uname_machine = f((n)->uname_machine); #endif ASSIGN_DIST(distri, node, ); /* Create distri hashtable if needed. */ GHashTable *ht_packages = g_hash_table_lookup(ht_distris, &distri); if(!ht_packages) { ht_packages = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); Distri *ndistri = g_malloc(sizeof(Distri)); #ifndef NDEBUG ndistri->_type = T_DISTRI; #endif ASSIGN_DIST(*ndistri, node, g_strdup); g_hash_table_insert(ht_distris, ndistri, ht_packages); } /* Traverse package list and count installed versions. */ gpointer data[2]; data[0] = ht_packages; data[1] = node; g_list_foreach(node->packages, add_pkg, data); } /* Remove PkgNode from package hashtable */ static void rem_pkg(gpointer data, gpointer user_data) { PkgNode *pkg = (PkgNode *)data; GHashTable *ht_packages = (GHashTable *)(((gpointer *)user_data)[0]); HostNode *node = (HostNode *)(((gpointer *)user_data)[1]); ASSERT_TYPE(pkg, T_PKGNODE); ASSERT_TYPE(node, T_HOSTNODE); if (!test_pkg(pkg)) return; /* Lookup packages hashtable. */ GList *l_versions = g_hash_table_lookup(ht_packages, pkg->package); if(!l_versions) return; /* Lookup version list. */ gchar *v = NULL; if(((pkg->flag & HOST_STATUS_PKGUPDATE) || (pkg->flag & HOST_STATUS_PKGKEPTBACK)) && (pkg->data)) v = pkg->data; else v = pkg->version; Version _v; #ifndef NDEBUG _v._type = T_VERSION; #endif _v.version = v; Version *vers = NULL; GList *l_v = g_list_find_custom(l_versions, &_v, cmp_vers); if (l_v) { vers = (Version *)l_v->data; ASSERT_TYPE(vers, T_VERSION); } if(!vers) return; vers->nodes = g_list_remove(vers->nodes, node); } /* Remove package data of HostNode from the hashtable leaves. */ void autoref_rem_host_info(HostNode *node) { Distri distri; if(!test_hostnode(node)) return; if (!ht_distris) return; ASSIGN_DIST(distri, node, ); /* Lookup distri hashtable. */ GHashTable *ht_packages = g_hash_table_lookup(ht_distris, &distri); if(!ht_packages) return; /* Traverse package list and count installed versions. */ gpointer data[2]; data[0] = ht_packages; data[1] = node; g_list_foreach(node->packages, rem_pkg, data); } /* Put a HostNode into refresh. */ static void trigger_refresh(gpointer data, gpointer user_data) { HostNode *node = (HostNode *)data; node->category = C_REFRESH_REQUIRED; } /* Record list of nodes which needs to be refreshed. */ GList *refresh_nodes = NULL; /** * Add node to refresh list if: * - last refresh is older than the time this version was * seen by apt-dater first * - the node is allowed to refresh **/ static void check_refresh(gpointer data, gpointer user_data) { HostNode *node = (HostNode *)data; int *ts_first = (int *)user_data; ASSERT_TYPE(node, T_HOSTNODE); if(node && (node->last_upd < *ts_first) && ((node->forbid & HOST_FORBID_REFRESH) == 0) && (g_list_find(refresh_nodes, node) == NULL)) refresh_nodes = g_list_prepend(refresh_nodes, node); } /* Trigger refresh for any node which has older version data. */ static void add_refresh(gpointer value, gpointer user_data) { Version *version = (Version *)value; ASSERT_TYPE(version, T_VERSION); g_list_foreach(version->nodes, check_refresh, user_data); } /* Check if refresh is needed for each package. */ static void trigger_package(gpointer key, gpointer value, gpointer user_data) { GList *l_versions = (GList *)value; GHashTable *ht_packages = (GHashTable *)user_data; if(l_versions == NULL) return; l_versions = g_list_sort(l_versions, cmp_vers); if (value != l_versions) g_hash_table_insert(ht_packages, g_strdup(key), l_versions); Version *newest = (Version *)(l_versions->data); ASSERT_TYPE(newest, T_VERSION); GList *l_oldvers = g_list_next(l_versions); /* Only one version known => nothing todo. */ if(l_oldvers == NULL) return; /* Any host, which has last_upd < version->ts_first needs to be refreshed. */ g_list_foreach(l_oldvers, add_refresh, &( newest -> ts_first )); } /* Start refresh stuff for each distri. */ static void trigger_distri(gpointer key, gpointer value, gpointer user_data) { GHashTable *ht_packages = (GHashTable *)value; g_hash_table_foreach(ht_packages, trigger_package, ht_packages); } /** * This function is called by refreshStats when no * host remains in IN_REFRESH state. **/ guint autoref_trigger_auto() { if (ht_distris) { g_hash_table_foreach(ht_distris, trigger_distri, NULL); guint ret = g_list_length(refresh_nodes); if (ret) { /* Refresh now! */ g_list_foreach(refresh_nodes, trigger_refresh, NULL); g_list_free(refresh_nodes); refresh_nodes = NULL; return ret; } } return 0; } #endif apt-dater/src/apt-dater.c0000664000175000017500000000362015131461765013353 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "apt-dater.h" #include "keyfiles.h" #include "ui.h" #include "stats.h" #include "sighandler.h" #include "lock.h" #include "env.h" #ifdef FEAT_XMLREPORT #include "report.h" #endif #ifndef SOURCE_DATE_UTC #error SOURCE_DATE_UTC is undefined! #endif #define VERSTEXT PACKAGE_STRING " - " SOURCE_DATE_UTC "\n\n" \ "Copyright Holder: IBH IT-Service GmbH [https://www.ibh.net/]\n\n" \ "This program is free software; you can redistribute it and/or modify\n" \ "it under the terms of the GNU General Public License as published by\n" \ "the Free Software Foundation; either version 2 of the License, or\n" \ "(at your option) any later version.\n\n" \ "Send bug reports to " PACKAGE_BUGREPORT ".\n\n" CfgFile *cfg = NULL; GMainLoop *loop = NULL; gboolean rebuilddl = FALSE; time_t oldest_st_mtime; apt-dater/src/screen.c0000664000175000017500000001201315131461765012745 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef FEAT_TMUX #include "screen.h" #include "parsecmd.h" #include "history.h" static struct passwd *pw = NULL; const static gchar * screen_get_sdir() { static gchar sdir[PATH_MAX]; if (g_strcmp0(getenv("SCREENDIR"), NULL) != 0) { g_strlcpy(sdir, getenv("SCREENDIR"), sizeof(sdir)); return sdir; } #ifndef __APPLE__ if (!pw) pw = getpwuid(getuid()); if (!pw) return NULL; g_snprintf(sdir, sizeof(sdir), SCREEN_SDFORMT, SCREEN_SOCKPATH, pw->pw_name); #else g_snprintf(sdir, sizeof(sdir), SCREEN_SDFORMT, SCREEN_SOCKPATH); #endif return sdir; } gboolean screen_get_sessions(HostNode *n) { if (n->screens) { g_list_free(n->screens); n->screens = NULL; } const gchar *sdir = screen_get_sdir(); if (!sdir) return FALSE; GDir *d = g_dir_open(sdir, 0, NULL); if (!d) { return FALSE; } gchar *search = g_strdup_printf(SCREEN_SOCKPRE"%s_%s_%d", n->ssh_user, n->hostname, n->ssh_port); const gchar *f; while ((f = g_dir_read_name(d))) { gchar *fn = g_strdup_printf("%s/%s", sdir, f); if (g_file_test(fn, G_FILE_TEST_EXISTS)) { gint pid = atoi(f); char *name = strchr(f, '.'); if ((pid > 1) && (name) && (strcmp(name+1, search) == 0)) { SessNode *s = g_new0(SessNode, 1); #ifndef NDEBUG s->_type = T_SESSNODE; #endif s->pid = pid; stat(fn, &s->st); s->attached = s->st.st_mode & S_IXUSR; n->screens = g_list_prepend(n->screens, s); } } g_free(fn); } g_dir_close(d); g_free(search); return g_list_length(n->screens) > 0; } gchar ** screen_new(HostNode *n, const gboolean detached) { gchar **_argv = (gchar **) g_malloc0(sizeof(gchar *) * 8); gchar *title = parse_string(cfg->screentitle, n); _argv[0] = g_strdup(SCREEN_BINARY); _argv[1] = g_strdup_printf("-%sS", detached ? "dm" : ""); _argv[2] = g_strdup_printf(SCREEN_SOCKPRE"%s_%s_%d", n->ssh_user, n->hostname, n->ssh_port); _argv[3] = g_strdup("-t"); _argv[4] = title; _argv[5] = g_strdup("-c"); _argv[6] = g_strdup(cfg->screenrcfile); _argv[7] = NULL; return _argv; } static gchar ** screen_attach_cmd(const SessNode *s, const gboolean shared) { gchar **_argv = (gchar **) g_malloc0(sizeof(gchar *) * 4); _argv[0] = g_strdup(SCREEN_BINARY); _argv[1] = g_strdup_printf("-r%s", shared ? "x" : ""); _argv[2] = g_strdup_printf("%d", s->pid); _argv[3] = NULL; return _argv; } gboolean screen_attach(HostNode *n, const SessNode *s, const gboolean shared) { gboolean r; GError *error = NULL; gchar **argv = screen_attach_cmd(s, shared); g_assert(n); r = g_spawn_sync(g_getenv ("HOME"), argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } g_strfreev(argv); #ifdef FEAT_HISTORY if(n->parse_result && !screen_get_sessions(n)) { n->parse_result = FALSE; return history_ts_failed(cfg, n); } #endif return FALSE; } static gchar ** screen_dump_cmd(const SessNode *s, const gchar *fn) { gchar **_argv = (gchar **) g_malloc0(sizeof(gchar *) * 7); _argv[0] = g_strdup(SCREEN_BINARY); _argv[1] = g_strdup("-S"); _argv[2] = g_strdup_printf("%d", s->pid); _argv[3] = g_strdup("-X"); _argv[4] = g_strdup("hardcopy"); _argv[5] = g_strdup(fn); _argv[6] = NULL; return _argv; } gchar * screen_get_dump(const SessNode *s) { gboolean r; GError *error = NULL; gchar *dump_fn = g_strdup_printf("%s/dump-XXXXXX", g_get_tmp_dir()); gint fd = g_mkstemp(dump_fn); gchar **argv = screen_dump_cmd(s, dump_fn); if(fd == -1) return NULL; r = g_spawn_sync(g_getenv ("HOME"), argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error); if(r == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } g_strfreev(argv); gchar *c = NULL; g_file_get_contents(dump_fn, &c, NULL, NULL); close(fd); g_unlink(dump_fn); return c; } #endif /* !FEAT_TMUX */ apt-dater/src/adsh.c0000664000175000017500000001032615131461765012412 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "apt-dater.h" #include "keyfiles.h" #include "ui.h" #include "stats.h" #include "sighandler.h" #include "lock.h" #include "env.h" #include "ttymux.h" #ifndef SOURCE_DATE_UTC #error SOURCE_DATE_UTC is undefined! #endif #define VERSTEXT PACKAGE_STRING " - " SOURCE_DATE_UTC "\n\n" \ "Copyright Holder: IBH IT-Service GmbH [https://www.ibh.net/]\n\n" \ "This program is free software; you can redistribute it and/or modify\n" \ "it under the terms of the GNU General Public License as published by\n" \ "the Free Software Foundation; either version 2 of the License, or\n" \ "(at your option) any later version.\n\n" \ "Send bug reports to " PACKAGE_BUGREPORT ".\n\n" typedef struct _hostfind { gchar *ssh_host; gint ssh_port; } HostFind; gint adsh_find(gconstpointer phost, gconstpointer ptarget) { HostNode *host = (HostNode *)phost; HostFind *target = (HostFind *)ptarget; if(strcasecmp((host->ssh_host ? host->ssh_host : host->hostname), target->ssh_host) == 0 && host->ssh_port == target->ssh_port) { return 0; } return 1; } int main(int argc, char **argv, char **envp) { int opts; gchar *cfgfilename = NULL; gchar *cfgdirname = NULL; GList *hosts = NULL; #ifdef HAVE_GETTEXT setlocale(LC_ALL, ""); textdomain(PACKAGE); #endif #if !GLIB_CHECK_VERSION(2, 36, 0) /* since glib 2.36 this is done automatically */ g_type_init(); #endif cfgdirname = g_strdup_printf("%s/%s", g_get_user_config_dir(), PACKAGE); if(!cfgdirname) g_error(_("Out of memory.")); cfgfilename = g_strdup_printf("%s/apt-dater.xml", cfgdirname); if(!cfgfilename) g_error(_("Out of memory.")); g_set_prgname(PACKAGE); g_set_application_name(PACKAGE_STRING); if(chkForInitialConfig(cfgdirname, cfgfilename)) g_warning(_("Failed to create initial configuration file %s."), cfgfilename); HostFind adsh_target; adsh_target.ssh_port = 0; opterr = 0; while ((opts = getopt(argc, argv, "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:E:F:I:KL:MNO:PQ:R:S:TVw:W:XYy")) != EOF) { switch (opts) { case 'p': adsh_target.ssh_port = atoi(optarg); break; } } int ac = argc - optind; char **av = argv + optind; if(!cfgfilename) g_error(_("Out of memory.")); cfg = initialConfig(); if(!(loadConfig(cfgfilename, cfg))) { g_printerr(_("Error on loading config file %s\n"), cfgfilename); exit(EXIT_FAILURE); } if(!(hosts = (GList *) loadHosts(cfg->hostsfile))) { g_printerr(_("Error on loading config file %s\n"), cfg->hostsfile); exit(EXIT_FAILURE); } env_init(envp); if(ac > 0) { adsh_target.ssh_host = av[0]; if(strrchr(adsh_target.ssh_host, '@')) { adsh_target.ssh_host = strrchr(adsh_target.ssh_host, '@'); } GList *adsh_host = g_list_find_custom(hosts, &adsh_target, adsh_find); if(adsh_host) { HostNode *n = adsh_host->data; HistoryEntry he; he.ts = time(NULL); he.maintainer = maintainer; he.action = "adsh"; he.data = NULL; gchar **env = env_build(n, "adsh", NULL, &he); execve(PKGLIBDIR"/cmd", argv, env); perror("Failed to run "PKGLIBDIR"/cmd"); exit(EXIT_FAILURE); } } execve("/usr/bin/ssh", argv, envp); exit(EXIT_FAILURE); } apt-dater/src/ui.c0000664000175000017500000025457315131461765012126 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "apt-dater.h" #include "ui.h" #include "colors.h" #include "completion.h" #include "ttymux.h" #include "exec.h" #include "stats.h" #include "keyfiles.h" #include "tag.h" #include "sighandler.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_TCLFILTER #include #endif #ifdef FEAT_RUNCUST #include "runcust.h" #endif static GList *drawlist = NULL; gchar *drawCategories[] = { N_("Updates pending"), N_("Up to date"), N_("Broken packages"), N_("Refresh required"), N_("In refresh"), N_("Sessions"), #ifdef FEAT_TCLFILTER N_("Filtered"), #endif N_("Unknown"), NULL}; static gchar *incategory = NULL; static gchar *ingroup = NULL; static HostNode *inhost = NULL; static gint bottomDrawLine; static WINDOW *win_dump = NULL; static gboolean dump_screen = FALSE; gchar maintainer[48]; #ifdef FEAT_TCLFILTER gchar filterexp[BUF_MAX_LEN]; #endif gint sc_mask = 0; static Completion* dlCompl = NULL; #ifdef FEAT_TCLFILTER static Tcl_Interp *tcl_interp = NULL; #endif #ifdef FEAT_TCLFILTER typedef enum { TCLM_STRING, TCLM_INT, TCLM_IGNORE, } ETCLMAPPING; typedef enum { TCLMK_CATEGORY, TCLMK_GROUP, TCLMK_HOSTNAME, TCLMK_COMMENT, TCLMK_KERNEL, TCLMK_LSBCNAME, TCLMK_LSBDISTRI, TCLMK_LSBREL, TCLMK_UNKERNEL, TCLMK_UNMACHINE, TCLMK_VIRT, TCLMK_FORBID, #ifdef FEAT_CLUSTERS TCLMK_CLUSTERS, #endif TCLMK_EXTRAS, TCLMK_FLAGS, TCLMK_HOLDS, TCLMK_INSTALLED, TCLMK_UPDATES, TCLMK_BROKENS, } ETCLMAPKEYS; struct TCLMapping { gint code; gchar *name; ETCLMAPPING type; }; const static struct TCLMapping tclmap[] = { {TCLMK_CATEGORY , "cat" , TCLM_INT}, {TCLMK_GROUP , "group" , TCLM_STRING}, {TCLMK_HOSTNAME , "hostname" , TCLM_STRING}, {TCLMK_COMMENT , "comment" , TCLM_STRING}, {TCLMK_KERNEL , "kernel" , TCLM_STRING}, {TCLMK_LSBCNAME , "lsb_cname" , TCLM_STRING}, {TCLMK_LSBDISTRI, "lsb_distri", TCLM_STRING}, {TCLMK_LSBREL , "lsb_rel" , TCLM_STRING}, {TCLMK_VIRT , "virt" , TCLM_STRING}, {TCLMK_FORBID , "forbid" , TCLM_INT}, #ifdef FEAT_CLUSTERS {TCLMK_CLUSTERS , "clusters" , TCLM_IGNORE}, #endif {TCLMK_EXTRAS , "extras" , TCLM_IGNORE}, {TCLMK_FLAGS , "flags" , TCLM_IGNORE}, {TCLMK_HOLDS , "holds" , TCLM_IGNORE}, {TCLMK_INSTALLED, "installed" , TCLM_IGNORE}, {TCLMK_UPDATES , "updates" , TCLM_IGNORE}, {TCLMK_BROKENS , "brokens" , TCLM_IGNORE}, {0 , NULL, 0}, }; #endif struct ShortCut { EShortCuts sc; int keycode; gchar *key; gchar *descr; gboolean visible; EVisKeyMask id; }; static struct ShortCut shortCuts[] = { {SC_KEY_LEFT, KEY_LEFT, N_(""), N_("shrink node") , FALSE, 0}, {SC_KEY_LEFT2, 'h', "h", N_("shrink node") , FALSE, 0}, {SC_KEY_RIGHT, KEY_RIGHT, N_(""), N_("expand node") , FALSE, 0}, {SC_KEY_RIGHT2, 'l', "l", N_("expand node") , FALSE, 0}, {SC_KEY_SPACE, ' ', N_(""), N_("shrink/expand node") , FALSE, 0}, {SC_KEY_RETURN, KEY_RETURN, N_(""), N_("shrink/expand node") , FALSE, 0}, {SC_KEY_ENTER, KEY_ENTER, N_(""), N_("shrink/expand node") , FALSE, 0}, {SC_KEY_UP, KEY_UP, N_(""), N_("move up") , FALSE, 0}, {SC_KEY_UP2, 'k', "k", N_("move up") , FALSE, 0}, {SC_KEY_DOWN, KEY_DOWN, N_(""), N_("move down") , FALSE, 0}, {SC_KEY_DOWN2, 'j', "j", N_("move down") , FALSE, 0}, {SC_KEY_HOME, KEY_HOME, N_(""), N_("move to the top") , FALSE, 0}, {SC_KEY_END, KEY_END, N_(""), N_("move to the end") , FALSE, 0}, {SC_KEY_PPAGE, KEY_PPAGE, N_(""), N_("previous page") , FALSE, 0}, {SC_KEY_NPAGE, KEY_NPAGE, N_(""), N_("next page") , FALSE, 0}, {SC_KEY_PLUS, '+', "+", N_("shrink/expand node") , FALSE, 0}, {SC_KEY_QUIT, 'q', "q" , N_("quit") , TRUE , 0}, {SC_KEY_HELP, '?', "?" , N_("help") , TRUE , 0}, {SC_KEY_FIND, '/', "/" , N_("search host") , FALSE , 0}, #ifdef FEAT_TCLFILTER {SC_KEY_FILTER, 'f', "f" , N_("filter hosts") , FALSE, 0}, #endif {SC_KEY_ATTACH, 'a', "a" , N_("attach session") , FALSE, VK_ATTACH}, {SC_KEY_CONNECT, 'c', "c" , N_("connect host") , FALSE, VK_CONNECT}, {SC_KEY_FILETRANS, 'C', "C" , N_("file transfer") , FALSE, 0}, {SC_KEY_TOGGLEDUMPS, 'd', "d" , N_("toggle dumps") , FALSE, VK_DUMP}, {SC_KEY_REFRESH, 'g', "g" , N_("refresh host") , FALSE, VK_REFRESH}, {SC_KEY_ERRDIAG, 'e', "e" , N_("failure diagnostic") , FALSE, VK_ERRDIAG}, {SC_KEY_INSTALL, 'i', "i" , N_("install pkg") , FALSE, VK_INSTALL}, {SC_KEY_UPGRADE, 'u', "u" , N_("upgrade host(s)") , FALSE, VK_UPGRADE}, {SC_KEY_MORE, 'm', "m" , N_("host details") , FALSE, 0}, #ifdef FEAT_HISTORY {SC_KEY_HISTORY, 'H', "H", N_("host history") , FALSE, 0}, {SC_KEY_PLAY, 'p', "p" , N_("play") , FALSE, VK_PLAY}, {SC_KEY_LESS, 'L', "L" , N_("display with less") , FALSE, VK_LESS}, #endif {SC_KEY_NEXTSESS, 'n', "n" , N_("next detached session") , FALSE, 0}, {SC_KEY_CYCLESESS, 'N', "N" , N_("cycle detached sessions") , FALSE, 0}, #ifdef FEAT_RUNCUST {SC_KEY_RUNCUST, 'r', "r" , N_("run custom command") , FALSE, VK_RUNCUST}, #endif {SC_KEY_TAG, 't', "t" , N_("tag current host") , FALSE, 0}, {SC_KEY_TAGMATCH, 'T', "T" , N_("tag all hosts matching") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~c tag by codename") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~d tag by distribution") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~f tag by host flags") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~g tag by group") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~p tag by packages") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~u tag by updates") , FALSE, 0}, {SC_KEY_UNUSED, KEY_MAX, "" , N_(" ~A tag all hosts") , FALSE, 0}, {SC_KEY_UNTAGMATCH, ctrl('T'), "^T" , N_("untag all hosts matching") , FALSE, 0}, {SC_KEY_TAGACTION, ';', ";" , N_("apply next function to tagged hosts") , FALSE, 0}, {SC_MAX, 0, NULL, NULL, FALSE, 0}, }; const struct HostFlag hostFlags[] = { {HOST_STATUS_PKGKEPTBACK , "h", N_("some packages are kept back")}, {HOST_STATUS_PKGEXTRA , "x", N_("extra packages are installed")}, {HOST_STATUS_KERNELABIUPGR , "k", N_("pending kernel upgrade (ABI compatible)")}, {HOST_STATUS_KERNELVERUPGR , "K", N_("pending kernel upgrade")}, {HOST_STATUS_KERNELUNKNOWN , "?", N_("unknown kernel upgrade state")}, {HOST_STATUS_VIRTUALIZED , "v", N_("this is a virtualized machine")}, #ifdef FEAT_CLUSTERS {HOST_STATUS_CLUSTERED , "C", N_("this machine is part of a cluster")}, #endif {0 , NULL, NULL}, }; static int getKeyForShortcut(EShortCuts sc) { gint i; for(i = 0; shortCuts[i].keycode; i++) if(shortCuts[i].sc == sc) return shortCuts[i].keycode; return -1; } static gboolean freeDl(GList *dl) { if(dl) { g_list_foreach(dl, (GFunc) freeDrawNode, NULL); g_list_free(dl); } else return FALSE; return TRUE; } int getnLine(WINDOW *win, gchar *str, gint n, gboolean usews) { gint cpos = 0, slen = 0, i; int ch = 0, cy, cx, sx, sy; gchar *modstr = NULL; gboolean dobeep; WINDOW *wp = NULL; if(!str || !win || !n) return (0); if(n > INPUT_MAX) n = INPUT_MAX; modstr = g_malloc0(n+1); if(!modstr) return(0); wp = newpad(1, n); if(!wp) { g_free(modstr); return(0); } enableInput(); keypad(wp, TRUE); noecho(); wrefresh(win); getyx(win, sy, sx); getbegyx(win, cy, cx); sy += cy; sx += cx; while(ch != KEY_RETURN && ch != KEY_ENTER) { getyx(wp, cy, cx); prefresh(wp, 0, cx+sx - COLS < 0 ? 0 : cx+sx+1 - COLS, sy, sx, sy, COLS-1); ch = wgetch(wp); dobeep = TRUE; if((isascii(ch) && isprint(ch)) || (ch == '\t' && usews == TRUE)) { if(slen < n-1) { if(cpos == slen) { modstr[cpos] = ch; waddch(wp, modstr[cpos++]); slen++; } else { bcopy(&modstr[cpos], &modstr[cpos+1], slen-cpos); modstr[cpos] = ch; winsch(wp, modstr[cpos++]); wmove(wp, cy, ++cx); slen++; } dobeep = FALSE; } } else { switch(ch) { case KEY_RETURN: case KEY_ENTER: continue; case KEY_BACKSPACE: if(slen > 0 && cpos > 0) { if(cpos == slen) modstr[--cpos] = 0; else { g_strlcpy(&modstr[cpos-1], &modstr[cpos], slen-cpos+1); cpos--; } mvwdelch(wp, cy, --cx); slen = strlen(modstr); dobeep = FALSE; } break; case KEY_DC: if(slen > 0 && cpos < slen) { dobeep = FALSE; g_strlcpy(&modstr[cpos], &modstr[cpos+1], slen-cpos+1); wdelch(wp); slen = strlen(modstr); } break; case KEY_LEFT: if(cpos > 0) { cpos--; wmove(wp, cy, --cx); dobeep = FALSE; } break; case KEY_RIGHT: if(cpos < slen && slen > 0) { cpos++; wmove(wp, cy, ++cx); dobeep = FALSE; } break; case ctrl('U'): /* Delete line */ cx+=(slen-cpos); for(i=slen;i > 0;i--) mvwdelch(wp, cy, --cx); memset(modstr, 0, n); cpos=0; dobeep = FALSE; break; case ctrl('A'): case KEY_HOME: cx-=cpos; cpos = 0; wmove(wp, cy, cx); dobeep = FALSE; break; case ctrl('E'): case KEY_END: cx+=(slen-cpos); cpos = slen; wmove(wp, cy, cx); dobeep = FALSE; break; case KEY_ESC: /* Abort */ case KEY_RESIZE: case ctrl('G'): disableInput(); g_free(modstr); delwin(wp); return(0); } /* switch(ch) */ } if(dobeep == TRUE) beep(); } disableInput(); memcpy(str, modstr, n+1); g_free(modstr); delwin(wp); return(ch); } static void setMenuEntries(gint mask) { mask |= sc_mask; gint i = -1; while(shortCuts[++i].key) { if(shortCuts[i].id == 0) continue; shortCuts[i].visible = mask & shortCuts[i].id; } } void freeDrawNode (DrawNode *n) { g_free(n); } void cleanBetween() { gint i; for(i=1; i < bottomDrawLine;i++) mvremln(i, 0, COLS); } DrawNode *getSelectedDrawNode() { GList *dl; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); if (!dl) return NULL; ASSERT_TYPE((DrawNode *)dl->data, T_DRAWNODE); return (DrawNode *)dl->data; } guint getCategoryNumber(gchar *category) { guint i = 0; while(*(drawCategories+i)) { if(*(drawCategories+i) == category) break; i++; } return(i); } void setHostsCategory(GList *hosts, Category matchCategory, gchar *matchGroup, Category setCategory) { GList *ho; ho = g_list_first(hosts); while(ho) { if(matchGroup) { if((((HostNode *) ho->data)->category == matchCategory) && (!g_ascii_strcasecmp(((HostNode *) ho->data)->group, matchGroup))) ((HostNode *) ho->data)->category = setCategory; } else if((((HostNode *) ho->data)->category == matchCategory)) ((HostNode *) ho->data)->category = setCategory; ho = g_list_next(ho); } } guint getHostGrpCatCnt(GList *hosts, gchar *group, Category category) { guint cnt = 0; GList *ho; ho = g_list_first(hosts); while(ho) { #ifdef FEAT_TCLFILTER if(category == C_FILTERED) { if((((HostNode *) ho->data)->filtered) && (!g_ascii_strcasecmp(((HostNode *) ho->data)->group, group))) cnt++; } else #endif if((((HostNode *) ho->data)->category == category) && (!g_ascii_strcasecmp(((HostNode *) ho->data)->group, group))) cnt++; ho = g_list_next(ho); } return(cnt); } guint getHostGrpCatTaggedCnt(GList *hosts, const gchar *group, Category category) { guint cnt = 0; GList *ho; ho = g_list_first(hosts); while(ho) { if((((HostNode *) ho->data)->category == category) && (!g_ascii_strcasecmp(((HostNode *) ho->data)->group, group)) && ((HostNode *) ho->data)->tagged == TRUE) cnt++; ho = g_list_next(ho); } return(cnt); } void disableInput() { noecho(); curs_set(0); leaveok(stdscr, TRUE); /* To slow down the idle process. */ timeout(200); } void enableInput() { echo(); curs_set(1); leaveok(stdscr, FALSE); timeout(-1); } void initUI () { printf("\033]0;" PACKAGE_STRING "\007"); initscr(); ui_start_color(); cbreak(); nonl(); disableInput(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); clear(); refresh(); bottomDrawLine = LINES-2; } void cleanUI () { clear(); refresh(); clear(); endwin(); } void blank() { printf("\033[2J"); fflush(stdout); } void drawMenu (gint mask) { setMenuEntries(mask); attron(uicolors[UI_COLOR_MENU]); move(0,0); remln(COLS); gint i=-1; gint p=COLS; while(shortCuts[++i].key) { if(!shortCuts[i].visible) continue; addnstr(shortCuts[i].key, MAX(0, p)); p -= strlen(shortCuts[i].key); addnstr(":", MAX(0, p)); p--; addnstr(_(shortCuts[i].descr), MAX(0, p)); p -= strlen(_(shortCuts[i].descr)); addnstr(" ", MAX(0, p)); p-=2; } attroff(uicolors[UI_COLOR_MENU]); } static void drawHelp () { WINDOW *wp = newpad(32+SC_MAX, COLS); gint l = 0; int wic = 0, pminrow = 0, kcquit = 'q'; keypad(wp, TRUE); wattron(wp, A_BOLD); mvwaddnstr(wp, l , 2, _("FLAG") , COLS - 2); mvwaddnstr(wp, l++, 16, _("DESCRIPTION"), COLS - 16); wattroff(wp, A_BOLD); gint i = -1; while(hostFlags[++i].flag) { mvwaddch (wp, l, 2, hostFlags[i].code[0]); mvwaddnstr(wp, l, 16, _(hostFlags[i].descr), COLS - 16); l++; } l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l , 2, _("KEY") , COLS - 2); mvwaddnstr(wp, l++, 16, _("DESCRIPTION"), COLS - 16); wattroff(wp, A_BOLD); i = -1; while(shortCuts[++i].key) { mvwaddnstr(wp, l, 2, shortCuts[i].key , COLS - 2); mvwaddnstr(wp, l, 16, _(shortCuts[i].descr), COLS - 16); l++; } for(i = 0; shortCuts[i].key; i++) if(shortCuts[i].sc == SC_KEY_QUIT) kcquit = shortCuts[i].keycode; pminrow = 0; prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); while((wic = tolower(wgetch(wp))) != kcquit) { if(wic == KEY_UP && pminrow) pminrow--; else if(wic == KEY_DOWN && pminrow < l-LINES+4) pminrow++; else if(wic == KEY_HOME) pminrow=0; else if(wic == KEY_END) pminrow=l-LINES+4; else if(wic == KEY_NPAGE) pminrow = pminrow+LINES-3 > (l-LINES+4) ? l-LINES+4 : pminrow+LINES-3; else if(wic == KEY_PPAGE) pminrow = pminrow-(LINES-3) < 0 ? 0 : pminrow-(LINES-3); prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); } delwin(wp); } static void drawHostDetails(HostNode *h) { WINDOW *wp = newpad(32 + h->nupdates + h->nholds + h->nextras + h->nbrokens + g_list_length(h->packages), COLS); char buf[0x1ff]; gint l = 0; int wic = 0, pminrow = 0, kcquit = 'q', i = 0; keypad(wp, TRUE); wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("HOST DETAILS"), COLS - 1); wattroff(wp, A_BOLD); mvwaddnstr(wp, l , 2, _("Group:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->group, COLS - 20); mvwaddnstr(wp, l , 2, _("Hostname:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->hostname, COLS - 20); if (h->comment) { mvwaddnstr(wp, l , 2, _("Comment:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->comment, COLS - 20); } if (h->virt) { mvwaddnstr(wp, l , 2, _("Machine Type:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->virt, COLS - 20); } if (h->uname_machine && h->uname_machine[0]) { mvwaddnstr(wp, l , 2, _("Architecture:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->uname_machine, COLS - 20); } if (h->uuid[0]) { mvwaddnstr(wp, l , 2, _("UUID:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->uuid, COLS - 20); } if (h->forbid & HOST_FORBID_MASK) { mvwaddnstr(wp, l , 2, _("Forbidden:"), COLS - 2); strcpy(buf, " "); if (h->forbid & HOST_FORBID_REFRESH) { strcat(buf, _("refresh")); } if (h->forbid & HOST_FORBID_UPGRADE) { if(strlen(buf) > 1) { strcat(buf, ", "); } strcat(buf, _("upgrade")); } if (h->forbid & HOST_FORBID_INSTALL) { if(strlen(buf) > 1) { strcat(buf, ", "); } strcat(buf, _("install")); } mvwaddnstr(wp, l++, 15, buf , COLS - 15); } l++; if (h->lsb_distributor) { mvwaddnstr(wp, l , 2, _("Distri:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->lsb_distributor, COLS - 20); if (h->lsb_codename) snprintf(buf, sizeof(buf), "%s (%s)", h->lsb_release, h->lsb_codename); else snprintf(buf, sizeof(buf), "%s", h->lsb_release); mvwaddnstr(wp, l , 2, _("Release:"), COLS - 2); mvwaddnstr(wp, l++, 20, buf, COLS - 20); } if (h->uname_kernel && h->uname_kernel[0]) { mvwaddnstr(wp, l , 2, _("Kernel name:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->uname_kernel, COLS - 20); } if (h->kernelrel) { mvwaddnstr(wp, l , 2, _("Kernel version:"), COLS - 2); mvwaddnstr(wp, l++, 20, h->kernelrel, COLS - 20); switch(h->status & (HOST_STATUS_KERNELABIUPGR | HOST_STATUS_KERNELVERUPGR)) { case HOST_STATUS_KERNELABIUPGR: strcpy(buf, _("(pending ABI compatible upgrade)")); break; case HOST_STATUS_KERNELVERUPGR: strcpy(buf, _("(pending upgrade)")); break; default: buf[0] = 0; break; } if(strlen(buf) > 0) mvwaddnstr(wp, l-1, 21 + strlen(h->kernelrel), buf, COLS - 21 - strlen(h->kernelrel)); } #ifdef FEAT_CLUSTERS if(g_list_length(h->clusters)) { l++; mvwaddnstr(wp, l , 2, _("Clusters: "), COLS - 2); GList *c = h->clusters; while(c) { mvwaddnstr(wp, l++, 20, c->data, COLS - 20); c = g_list_next(c); } } #endif if (g_list_length(h->packages)) { l++; mvwaddnstr(wp, l , 2, _("Packages: "), COLS - 2); snprintf(buf, sizeof(buf), "%d installed (%d update%s, %d hold back, %d broken, %d extra)", g_list_length(h->packages), h->nupdates, h->nupdates == 1 ? "" : "s", h->nholds, h->nbrokens, h->nextras); mvwaddnstr(wp, l++, 20, buf, COLS - 20); } if (h->nbrokens) { l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("BROKEN PACKAGES"), COLS - 1); wattroff(wp, A_BOLD); GList *p = g_list_first(h->packages); while(p) { PkgNode *pn = p->data; if (pn->flag & HOST_STATUS_PKGBROKEN) { mvwaddnstr(wp, l , 2, pn->package , MIN(33, COLS - 2)); snprintf(buf, sizeof(buf), "%s (%s)", pn->version, pn->data); mvwaddnstr(wp, l++, 36, buf, COLS - 36); } p = g_list_next(p); } } if (h->nupdates) { l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("UPDATE PACKAGES") , COLS - 1); wattroff(wp, A_BOLD); GList *p = g_list_first(h->packages); while(p) { PkgNode *pn = p->data; if (pn->flag & HOST_STATUS_PKGUPDATE) { mvwaddnstr(wp, l , 2, pn->package, MIN(33, COLS - 2)); snprintf(buf, sizeof(buf), "%s -> %s", pn->version, pn->data); mvwaddnstr(wp, l++, 36, buf, COLS - 36); } p = g_list_next(p); } } if (h->nholds) { l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("HOLD BACK PACKAGES") , COLS - 1); wattroff(wp, A_BOLD); GList *p = g_list_first(h->packages); while(p) { PkgNode *pn = p->data; if (pn->flag & HOST_STATUS_PKGKEPTBACK) { mvwaddnstr(wp, l , 2, pn->package, MIN(33, COLS - 2)); mvwaddnstr(wp, l++, 36, pn->version, COLS - 36); } p = g_list_next(p); } } if (h->nextras) { l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("EXTRA PACKAGES") , COLS - 1); wattroff(wp, A_BOLD); GList *p = g_list_first(h->packages); while(p) { PkgNode *pn = p->data; if (pn->flag & HOST_STATUS_PKGEXTRA) { mvwaddnstr(wp, l , 2, pn->package , MIN(33, COLS - 2)); mvwaddnstr(wp, l++, 36, pn->version , COLS - 36); } p = g_list_next(p); } } if (g_list_length(h->packages)) { l++; wattron(wp, A_BOLD); mvwaddnstr(wp, l++, 1, _("INSTALLED PACKAGES"), COLS - 1); wattroff(wp, A_BOLD); GList *p = g_list_first(h->packages); while(p) { PkgNode *pn = p->data; mvwaddnstr(wp, l , 2, pn->package , MIN(33, COLS - 2)); mvwaddnstr(wp, l++, 36, pn->version , COLS - 36); p = g_list_next(p); } } for(i = 0; shortCuts[i].key; i++) if(shortCuts[i].sc == SC_KEY_QUIT) kcquit = shortCuts[i].keycode; pminrow = 0; prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); while((wic = tolower(wgetch(wp))) != kcquit) { if(wic == KEY_UP && pminrow) pminrow--; else if(wic == KEY_DOWN && pminrow < l-LINES+4) pminrow++; else if(wic == KEY_HOME) pminrow=0; else if(wic == KEY_END) pminrow=l-LINES+4; else if(wic == KEY_NPAGE) pminrow = pminrow+LINES-3 > (l-LINES+4) ? l-LINES+4 : pminrow+LINES-3; else if(wic == KEY_PPAGE) pminrow = pminrow-(LINES-3) < 0 ? 0 : pminrow-(LINES-3); else if(wic == '?') drawHelp(); prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); } delwin(wp); } static void drawHostErrDiag(HostNode *h) { gchar *buf = getStatsContent(h); int l = 2; if(buf != NULL) { gchar *b = buf; while((b = strchr(b, '\n'))) { l++; b++; } b = buf; while((b = strchr(b, '\r'))) { *b = ' '; } } WINDOW *wp = newpad(MAX(LINES, l+2), COLS); int wic = 0, pminrow = 0, kcquit = 'q', i; keypad(wp, TRUE); wattron(wp, A_BOLD); mvwaddnstr(wp, 0, 1, _("FAILURE DIAGNOSTIC"), COLS - 1); wattroff(wp, A_BOLD); mvwaddstr(wp, 2, 0, buf); g_free(buf); for(i = 0; shortCuts[i].key; i++) if(shortCuts[i].sc == SC_KEY_QUIT) kcquit = shortCuts[i].keycode; prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); while((wic = tolower(wgetch(wp))) != kcquit) { if(wic == KEY_UP && pminrow) pminrow--; else if(wic == KEY_DOWN && pminrow < l-LINES+4) pminrow++; else if(wic == KEY_HOME) pminrow=0; else if(wic == KEY_END) pminrow=l-LINES+4; else if(wic == KEY_NPAGE) pminrow = pminrow+LINES-3 > (l-LINES+4) ? l-LINES+4 : pminrow+LINES-3; else if(wic == KEY_PPAGE) pminrow = pminrow-(LINES-3) < 0 ? 0 : pminrow-(LINES-3); else if(wic == '?') drawHelp(); prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); } delwin(wp); } void drawStatus (char *str, gboolean drawinfo) { char strinfo[42]; struct tm *tm_mtime; int mtime_pos; if(drawinfo == TRUE) { tm_mtime = localtime(&oldest_st_mtime); strftime(strinfo, sizeof(strinfo), _(" [Oldest: %x %X]"), tm_mtime); } attron(uicolors[UI_COLOR_STATUS]); move(bottomDrawLine, 0); remln(COLS); if(str) { addnstr(str, COLS); } if(drawinfo == TRUE) { mtime_pos = COLS < strlen(strinfo) ? COLS-1 : COLS-strlen(strinfo)-1; move(bottomDrawLine, mtime_pos); addnstr(strinfo, COLS-mtime_pos); } attroff(uicolors[UI_COLOR_STATUS]); } void drawQuery (const char *str, gulong display_ms) { attron(uicolors[UI_COLOR_QUERY]); mvaddstr(LINES - 1, 0, str); attroff(uicolors[UI_COLOR_QUERY]); if(display_ms) { refresh(); g_usleep(display_ms); mvremln(LINES - 1, 0, COLS); refresh(); } } gboolean queryString(const gchar *query, gchar *in, const gint size) { gint i; gboolean r; gint maxsize; enableInput(); drawQuery(query, 0); move(LINES - 1, strlen(query)); attron(uicolors[UI_COLOR_INPUT]); for(i = strlen(in)-1; i>=0; i--) ungetch(in[i]); maxsize = size > INPUT_MAX ? INPUT_MAX : size; r = getnLine(stdscr, in, maxsize, FALSE) == 0 ? FALSE : TRUE; disableInput(); attroff(uicolors[UI_COLOR_INPUT]); move(LINES - 1, 0); remln(COLS); return(r); } gboolean queryConfirm(const gchar *query, const gboolean enter_is_yes, gboolean *has_canceled) { int c; enableInput(); drawQuery(query, 0); move(LINES - 1, strlen(query)); attron(uicolors[UI_COLOR_INPUT]); c = getch(); attroff(uicolors[UI_COLOR_INPUT]); disableInput(); move(LINES - 1, 0); remln(COLS); if (has_canceled) { *has_canceled = ((c == _("c")[0]) || (c == _("C")[0]) || (c == 27)); } return ((c == _("y")[0]) || (c == _("Y")[0]) || (enter_is_yes == TRUE && (c == 0xd || c == KEY_ENTER))); } /* Reads a char from the terminal w/o ncurses stuff. */ gchar termGetChar() { struct termios to; struct termios tn; /* backup settings */ tcgetattr(STDIN_FILENO, &to); memcpy(&tn, &to, sizeof(to)); /* disable line buffer */ tn.c_lflag &= ~ICANON; tcsetattr(STDIN_FILENO, TCSANOW, &tn); gchar r = getchar(); /* restore line buffer */ tcsetattr(STDIN_FILENO, TCSANOW, &to); return r; } #ifdef FEAT_HISTORY static void drawHistoryLine(WINDOW *wp, const HistoryEntry *he, const gint l) { char buf[0x1ff]; const struct tm *ts = localtime(&(he->ts)); #define MAXCOLS(pos, s) (pos+s) <= COLS ? s : COLS-pos mvwremln(wp, l, 0, COLS); if(he->errpattern) mvwaddnstr(wp,l , 0, "F", MAXCOLS(0, 1)); snprintf(buf, sizeof(buf), "%4d", l+1); mvwaddnstr(wp, l , 1, buf, MAXCOLS(0, 4)); strftime(buf, sizeof(buf), "%x %X", ts); mvwaddnstr(wp, l , 7, buf, MAXCOLS(7, 17)); if(he->duration > 86400) snprintf(buf, sizeof(buf), "%dd", (he->duration/86400)); else if(he->duration > 3600) snprintf(buf, sizeof(buf), "%dh", (he->duration/3600)); else if(he->duration > 60) snprintf(buf, sizeof(buf), "%dmin", (he->duration/60)); else if(he->duration > 0) snprintf(buf, sizeof(buf), "%ds", he->duration); else strcpy(buf, "n/a"); mvwaddnstr(wp, l , 26, buf , MAXCOLS(26, 8)); mvwaddnstr(wp, l , 36, he->maintainer , MAXCOLS(36, 13)); mvwaddnstr(wp, l , 51, he->action , MAXCOLS(51, COLS-51)); if (he->data) mvwaddnstr(wp, l , 52+strlen(he->action), he->data, MAXCOLS(52+strlen(he->action), COLS- 52- strlen(he->action))); } static void drawHistoryEntries (HostNode *n) { gint l = 0; int wic = 0, pminrow = 0, i; guint sc = SC_MAX; char statusln[BUF_MAX_LEN]; GList *hel = NULL, *hep = NULL; WINDOW *wp = NULL; if(!n) return; hel = history_get_entries(cfg, inhost); if(!hel) { drawQuery(_("No history data available!"), G_USEC_PER_SEC); refresh(); return; } snprintf(statusln, BUF_MAX_LEN, g_list_length (hel) == 1 ? _("History of %s (%d entry available)") : _("History of %s (%d entries available)"), n->hostname ? n->hostname : "", g_list_length (hel)); drawStatus(statusln, FALSE); drawMenu(VK_PLAY|VK_LESS); refresh(); if(!(wp = newpad(g_list_length (hel) + LINES, COLS))) return; keypad(wp, TRUE); hep = g_list_first(hel); while(hep) { if(!l) wattron(wp, uicolors[UI_COLOR_SELECTOR]); drawHistoryLine(wp, (HistoryEntry *)hep->data, l++); wattroff(wp, uicolors[UI_COLOR_SELECTOR]); hep = g_list_next(hep); } gint crow = 0; gint orow = 0; gint mrow = g_list_length(hel) - 1; pminrow = 0; prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); while(sc != SC_KEY_QUIT) { wic = wgetch(wp); for(i = 0; shortCuts[i].keycode; i++) if(shortCuts[i].keycode == wic) sc = shortCuts[i].sc; orow = crow; if(sc == SC_KEY_UP && crow) { crow--; if(crow < pminrow && pminrow) pminrow--; } else if(sc == SC_KEY_DOWN && crow < mrow) { crow++; if(crow-pminrow > LINES-4 && pminrow < l-LINES+3) pminrow++; } else if(sc == SC_KEY_HOME) { crow=pminrow=0; } else if(sc == SC_KEY_END) { crow=mrow; if(pminrow+LINES-4 < mrow) pminrow=l-LINES+3; } else if(sc == SC_KEY_NPAGE) { if(mrow-crow (l-LINES+4) ? l-LINES+4 : pminrow+LINES-3; if(crow < 0) { crow=mrow; pminrow=0; } } else if(sc == SC_KEY_PPAGE) { if(crow-LINES+3<0) pminrow=crow=0; else { pminrow = pminrow-(LINES-3) < 0 ? 0 : pminrow-(LINES-3); crow=pminrow+(LINES-4); if(crow > mrow) pminrow=crow=0; } } else if(sc == SC_KEY_LESS) { ignoreSIGINT(TRUE); endwin(); blank(); HistoryEntry *he = (HistoryEntry *)(g_list_nth(hel, crow)->data); if(he->errpattern) history_show_less_search(he, he->errpattern); else history_show_less(he); ignoreSIGINT(FALSE); refresh(); } else if(sc == SC_KEY_PLAY) { ignoreSIGINT(TRUE); endwin(); blank(); history_show_replay((HistoryEntry *)(g_list_nth(hel, crow)->data)); printf("\n================[ %s ]================\n", _("replay terminated, press any key to continue")); fflush(stdout); termGetChar(); blank(); ignoreSIGINT(FALSE); } else if(sc == SC_KEY_HELP) { drawHelp(); } #ifdef KEY_RESIZE else if(wic == KEY_RESIZE) { sc = SC_KEY_QUIT; } #endif if(crow != orow) { drawHistoryLine(wp, (HistoryEntry *)(g_list_nth(hel, orow)->data), orow); orow = crow; wattron(wp, uicolors[UI_COLOR_SELECTOR]); drawHistoryLine(wp, (HistoryEntry *)(g_list_nth(hel, crow)->data), crow); wattroff(wp, uicolors[UI_COLOR_SELECTOR]); } prefresh(wp, pminrow, 0, 1, 0, LINES-3, COLS); } delwin(wp); history_free_hel(hel); } #endif void drawCategoryEntry (DrawNode *n) { char statusln[BUF_MAX_LEN]; attron(n->attrs); mvremln(n->scrpos, 0, COLS); mvaddstr(n->scrpos, 0, " ["); if(n->elements > 0) addch(n->elements > 0 && n->extended == FALSE ? '+' : '-'); else addch(' '); addstr("]"); if(n->etagged) addch('*'); else addch(' '); addnstr(_((char *) n->p), COLS); attroff(n->attrs); if(n->selected == TRUE) { if (n->elements > 1 || n->elements == 0) { snprintf(statusln, BUF_MAX_LEN, _("%d Hosts in status \"%s\""), n->elements, _((char *) n->p)); } else { snprintf(statusln, BUF_MAX_LEN, _("%d Host in status \"%s\""), n->elements, _((char *) n->p)); } drawMenu(VK_REFRESH); drawStatus(statusln, TRUE); } } void drawGroupEntry (DrawNode *n) { char statusln[BUF_MAX_LEN]; attron(n->attrs); mvremln(n->scrpos, 0, COLS); mvaddstr(n->scrpos, 2, " ["); addch(n->elements > 0 && n->extended == FALSE ? '+' : '-'); addstr("]"); if(n->etagged) addch('*'); else addch(' '); addnstr((char *) n->p, COLS); attroff(n->attrs); if(n->selected == TRUE) { if (n->elements > 1 || n->elements == 0) { snprintf(statusln, BUF_MAX_LEN, _("%d Hosts in status \"%s\""), n->elements, _(incategory)); } else { snprintf(statusln, BUF_MAX_LEN, _("%d Host in status \"%s\""), n->elements, _(incategory)); } drawMenu(VK_REFRESH); drawStatus(statusln, TRUE); } } void drawHostEntry (DrawNode *n) { char statusln[BUF_MAX_LEN]; gint mask = 0; char *hostentry; attron(n->attrs); mvremln(n->scrpos, 0, COLS); if (((HostNode *) n->p)->status & HOST_STATUS_LOCKED) { attron(uicolors[UI_COLOR_HOSTSTATUS]); mvaddstr(n->scrpos, 1, "L"); attroff(uicolors[UI_COLOR_HOSTSTATUS]); } else { move(n->scrpos, 1); attron(uicolors[UI_COLOR_HOSTSTATUS]); gint i = 0; while(hostFlags[i].flag) { if (((HostNode *) n->p)->status & hostFlags[i].flag) addch(hostFlags[i].code[0]); else addch(' '); i++; } attroff(uicolors[UI_COLOR_HOSTSTATUS]); } #ifdef FEAT_CLUSTERS mvaddstr(n->scrpos, 7, " ["); #else mvaddstr(n->scrpos, 6, " ["); #endif if(n->elements > 0) addch(n->elements > 0 && n->extended == FALSE ? '+' : '-'); else addch(' '); addstr("]"); if(((HostNode *) n->p)->tagged) addch('*'); else addch(' '); if(((HostNode *) n->p)->forbid & HOST_FORBID_MASK) attron(A_UNDERLINE); if(((HostNode *) n->p)->lsb_distributor) { if(!HOST_SSHPORT_SET((HostNode *) n->p)) hostentry = g_strdup_printf("%s (%s %s %s; %s)", ((HostNode *) n->p)->hostname, ((HostNode *) n->p)->lsb_distributor, ((HostNode *) n->p)->lsb_release, ((HostNode *) n->p)->lsb_codename, ((HostNode *) n->p)->kernelrel); else hostentry = g_strdup_printf("%s:%d (%s %s %s; %s)", ((HostNode *) n->p)->hostname, ((HostNode *) n->p)->ssh_port, ((HostNode *) n->p)->lsb_distributor, ((HostNode *) n->p)->lsb_release, ((HostNode *) n->p)->lsb_codename, ((HostNode *) n->p)->kernelrel); addnstr((char *) hostentry, COLS - 11); g_free(hostentry); } else { if(!HOST_SSHPORT_SET((HostNode *) n->p)) addnstr((char *) ((HostNode *) n->p)->hostname, COLS - 11); else { hostentry = g_strdup_printf("%s:%d", ((HostNode *) n->p)->hostname, ((HostNode *) n->p)->ssh_port); addnstr((char *) hostentry, COLS - 11); g_free(hostentry); } } if(((HostNode *) n->p)->forbid & HOST_FORBID_MASK) attroff(A_UNDERLINE); attroff(n->attrs); if(n->selected == TRUE) { switch(((HostNode *) n->p)->category) { case C_UPDATES_PENDING: mask = VK_CONNECT | VK_UPGRADE | VK_REFRESH | VK_INSTALL; #ifdef FEAT_RUNCUST mask |= VK_RUNCUST; #endif if (((HostNode *) n->p)->nupdates > 1 || ((HostNode *) n->p)->nupdates == 0) { snprintf(statusln, sizeof(statusln), _("%d Updates required"), ((HostNode *) n->p)->nupdates); } else { snprintf(statusln, sizeof(statusln), _("%d Update required"), ((HostNode *) n->p)->nupdates); } break; case C_UP_TO_DATE: mask = VK_CONNECT | VK_REFRESH | VK_INSTALL; #ifdef FEAT_RUNCUST mask |= VK_RUNCUST; #endif sprintf(statusln, _("No update required")); break; case C_BROKEN_PKGS: mask = VK_CONNECT | VK_REFRESH | VK_INSTALL; #ifdef FEAT_RUNCUST mask |= VK_RUNCUST; #endif if (((HostNode *) n->p)->nbrokens > 1 || ((HostNode *) n->p)->nbrokens == 0) { snprintf(statusln, sizeof(statusln), _("%d Broken packages"), ((HostNode *) n->p)->nbrokens); } else { snprintf(statusln, sizeof(statusln), _("%d Broken package"), ((HostNode *) n->p)->nbrokens); } break; case C_REFRESH_REQUIRED: mask = VK_CONNECT | VK_REFRESH | VK_INSTALL; #ifdef FEAT_RUNCUST mask |= VK_RUNCUST; #endif sprintf(statusln, _("Refresh required")); break; case C_REFRESH: sprintf(statusln, _("In refresh")); break; case C_SESSIONS: mask = VK_ATTACH; if (g_list_length(((HostNode *) n->p)->screens) != 1) { sprintf(statusln, _("%d sessions running"), g_list_length(((HostNode *) n->p)->screens)); } else { sprintf(statusln, _("%d session running"), g_list_length(((HostNode *) n->p)->screens)); } break; default: mask = VK_CONNECT | VK_REFRESH | VK_INSTALL | VK_ERRDIAG; #ifdef FEAT_RUNCUST mask |= VK_RUNCUST; #endif if(((HostNode *) n->p)->adperr == NULL) { sprintf(statusln, _("Status is unknown")); } else { sprintf(statusln, _("Error: %s"), ((HostNode *) n->p)->adperr); } break; } if (((HostNode *) n->p)->status & HOST_STATUS_LOCKED) strcat(statusln, _(" - host locked by another process")); drawMenu(mask); drawStatus(statusln, TRUE); } } void drawPackageEntry (DrawNode *n) { char statusln[BUF_MAX_LEN]; attron(n->attrs); mvremln(n->scrpos, 0, COLS); if(((PkgNode *) n->p)->flag & HOST_STATUS_PKGUPDATE) mvaddstr(n->scrpos, 7, "u:"); else if(((PkgNode *) n->p)->flag & HOST_STATUS_PKGKEPTBACK) mvaddstr(n->scrpos, 7, "h:"); else if(((PkgNode *) n->p)->flag & HOST_STATUS_PKGEXTRA) mvaddstr(n->scrpos, 7, "x:"); else if(((PkgNode *) n->p)->flag & HOST_STATUS_PKGBROKEN) { attron(A_BOLD); mvaddstr(n->scrpos, 7, "b:"); } mvaddnstr(n->scrpos, 10, (char *) ((PkgNode *) n->p)->package, COLS-10); attroff(A_BOLD); attroff(n->attrs); if(n->selected == TRUE) { if(((PkgNode *) n->p)->data) { if (((PkgNode *) n->p)->flag & HOST_STATUS_PKGBROKEN) snprintf(statusln, BUF_MAX_LEN, "%s (%s)", ((PkgNode *) n->p)->version, ((PkgNode *) n->p)->data); else snprintf(statusln, BUF_MAX_LEN, "%s -> %s", ((PkgNode *) n->p)->version, ((PkgNode *) n->p)->data); } else snprintf(statusln, BUF_MAX_LEN, "%s", ((PkgNode *) n->p)->version); drawMenu(VK_INSTALL); drawStatus(statusln, TRUE); } } static gboolean refreshDumpWindow (DrawNode *n) { gchar *dump = n->type == SESSION ? TTYMUX_GET_DUMP((SessNode *) n->p) : NULL; gchar *hostname = NULL; char h[BUF_MAX_LEN]; if(dump) { if (win_dump) { mvwaddstr(win_dump, 1, 0, dump); wattron(win_dump, uicolors[UI_COLOR_STATUS]); mvwremln(win_dump, 0, 0, COLS); if(n->parent && n->parent->type == HOST) hostname = ((HostNode *) n->parent->p)->hostname; snprintf(h, sizeof(h), _("Running session %s [%5d]:"), hostname ? hostname : "", ((SessNode *) n->p)->pid); mvwaddstr(win_dump, 0, 0, h); wattroff(win_dump, uicolors[UI_COLOR_STATUS]); wrefresh(win_dump); } g_free(dump); return TRUE; } else return FALSE; } void drawSessionEntry (DrawNode *n) { char h[BUF_MAX_LEN]; struct tm *tm_mtime; char statusln[BUF_MAX_LEN]; snprintf(h, sizeof(h), "%5d: ", ((SessNode *) n->p)->pid); tm_mtime = localtime(&((SessNode *) n->p)->st.st_mtime); strftime(&h[strlen(h)], sizeof(h)-strlen(h), _("%D %H:%M "), tm_mtime); snprintf(&h[strlen(h)], sizeof(h)-strlen(h), "(%s)", (TTYMUX_IS_ATTACHED((SessNode *) n->p) ? _("Attached") : _("Detached"))); attron(n->attrs); mvremln(n->scrpos, 0, COLS); mvaddnstr(n->scrpos, 7, h, COLS); attroff(n->attrs); if(n->selected == TRUE) { drawMenu(VK_ATTACH | VK_DUMP); if (dump_screen) { if(refreshDumpWindow (n) == FALSE) drawQuery(_("Could not read session dump."), G_USEC_PER_SEC); } else { snprintf(statusln, sizeof(statusln), "Session is %s", (TTYMUX_IS_ATTACHED((SessNode *) n->p) ? _("attached") : _("detached"))); drawStatus(statusln, TRUE); } } } void detectPos() { GList *dl; dl = g_list_first(drawlist); while(dl) { switch(((DrawNode *) dl->data)->type) { case CATEGORY: incategory = ((DrawNode *) dl->data)->p; break; case GROUP: ingroup = ((DrawNode *) dl->data)->p; break; case HOST: inhost = ((DrawNode *) dl->data)->p; break; default: break; } if(((DrawNode *) dl->data)->selected) return; dl = g_list_next(dl); } } gchar *getStrFromDrawNode (DrawNode *n) { gchar *ret = NULL; static gchar h[BUF_MAX_LEN]; struct tm *tm_mtime; switch(n->type) { case CATEGORY: ret = (gchar *) n->p; break; case GROUP: ret = (gchar *) n->p; break; case HOST: ret = (gchar * ) ((HostNode *) n->p)->hostname; break; case PKG: ret = (gchar * ) ((PkgNode *) n->p)->package; break; case SESSION: snprintf(h, sizeof(h), "%5d: ", ((SessNode *) n->p)->pid); tm_mtime = localtime(&((SessNode *) n->p)->st.st_mtime); strftime(&h[strlen(h)], sizeof(h)-strlen(h), _("%D %H:%M "), tm_mtime); snprintf(&h[strlen(h)], sizeof(h)-strlen(h), "(%s)", (TTYMUX_IS_ATTACHED((SessNode *) n->p) ? _("Attached") : _("Detached"))); ret = h; break; default: break; } return ret; } void drawEntry (DrawNode *n) { if(n->scrpos == 0 || n->scrpos >= bottomDrawLine) return; switch(n->type) { case CATEGORY: drawCategoryEntry(n); break; case GROUP: drawGroupEntry(n); break; case HOST: drawHostEntry(n); break; case PKG: drawPackageEntry(n); break; case SESSION: drawSessionEntry(n); break; default: break; } } void refreshDraw() { /* clear(); Don't clear stupid the hole screen. */ cleanBetween(); detectPos(); if(dump_screen) { if(getSelectedDrawNode()->type == SESSION) { if (!win_dump) { win_dump= newwin((LINES/2),COLS, LINES-(LINES/2), 0); scrollok(win_dump, TRUE); syncok(win_dump, TRUE); reorderScrpos(1); } } else { if(win_dump) { delwin(win_dump); win_dump = NULL; reorderScrpos(1); } } } else { if(win_dump) { delwin(win_dump); win_dump = NULL; reorderScrpos(1); } } g_list_foreach(drawlist, (GFunc) drawEntry, NULL); refresh(); } void refreshUI() { refresh(); } void setEntryActiveStatus(DrawNode *n, gboolean active) { if(!n) return; if(active == TRUE) { n->selected = TRUE; n->attrs|=uicolors[UI_COLOR_SELECTOR]; } else { n->selected = FALSE; if(n->attrs & uicolors[UI_COLOR_SELECTOR]) n->attrs^=uicolors[UI_COLOR_SELECTOR]; } } guint getHostCatCnt(GList *hosts, Category category) { guint cnt = 0; GList *ho; ho = g_list_first(hosts); while(ho) { #ifdef FEAT_TCLFILTER if(category == C_FILTERED) { if(((HostNode *) ho->data)->filtered) cnt++; } else #endif if(((HostNode *) ho->data)->category == category) cnt++; ho = g_list_next(ho); } return(cnt); } guint getHostCatTaggedCnt(GList *hosts, Category category) { guint cnt = 0; GList *ho; ho = g_list_first(hosts); while(ho) { if(((HostNode *) ho->data)->category == category && ((HostNode *) ho->data)->tagged == TRUE) cnt++; ho = g_list_next(ho); } return(cnt); } void checkSelected() { DrawNode *dn; GList *dl; dn = getSelectedDrawNode(); if (!dn) { dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->scrpos < bottomDrawLine)) dl = g_list_next(dl); if (!dl) dl = g_list_last(drawlist); setEntryActiveStatus((DrawNode *) dl->data, TRUE); return; } if (dn -> scrpos == 0 || dn -> scrpos > bottomDrawLine) { dl = g_list_first(drawlist); while(dl && (dl->data != dn)) dl = g_list_next(dl); gint p = bottomDrawLine-1; while(dl && p) { ((DrawNode *)dl->data)->scrpos = p--; dl = g_list_previous(dl); } return; } } void reorderScrpos(guint startat) { guint count = 0; GList *dl; dl = g_list_first(drawlist); while(dl) { if (((DrawNode *) dl->data)->scrpos == startat) while (++count < bottomDrawLine) { if(!dl) return; ((DrawNode *) dl->data)->scrpos = count; dl = g_list_next(dl); } if(dl) ((DrawNode *) dl->data )->scrpos = 0; dl = g_list_next(dl); } checkSelected(); } void buildIntialDrawList(GList *hosts) { guint i = 0; DrawNode *drawnode; while(*(drawCategories+i)) { drawnode = g_new0(DrawNode, 1); #ifndef NDEBUG drawnode->_type = T_DRAWNODE; #endif drawnode->p = *(drawCategories+i); drawnode->type = CATEGORY; drawnode->extended = FALSE; drawnode->selected = i == 0 ? TRUE : FALSE; drawnode->scrpos = i == 0 ? 1 : 0; drawnode->elements = getHostCatCnt(hosts, i); drawnode->etagged = getHostCatTaggedCnt(hosts, i); drawnode->parent = NULL; if((i == (Category) C_UPDATES_PENDING || i == (Category) C_BROKEN_PKGS || i == (Category) C_SESSIONS ) && drawnode->elements > 0) drawnode->attrs = A_BOLD; else drawnode->attrs = A_NORMAL; if(drawnode->selected == TRUE) setEntryActiveStatus(drawnode, TRUE); drawlist = g_list_append(drawlist, drawnode); i++; } } void notifyUser() { if(cfg->beep) beep(); if(cfg->flash) flash(); } void doUI (GList *hosts) { #ifdef FEAT_TCLFILTER /* Prepare TCL interpreter */ filterexp[sizeof(filterexp) - 1] = 0; if(cfg->filterexp) strncpy(filterexp, cfg->filterexp, sizeof(filterexp) - 1); else filterexp[0] = 0; tcl_interp = Tcl_CreateInterp(); if(cfg->filterfile) Tcl_EvalFile(tcl_interp, cfg->filterfile); #endif initUI(); /* Create completion list. */ /* hstCompl = g_completion_new(compHost); g_completion_add_items(hstCompl, hosts); */ /* Draw the host entries intial. */ buildIntialDrawList(hosts); reorderScrpos(1); refreshDraw(); /* Create completion list. */ dlCompl = completion_init(); completion_set_entries(dlCompl, drawlist); const gchar *m = getenv("MAINTAINER"); maintainer[sizeof(maintainer) - 1 ] = 0; if (m) strncpy(maintainer, m, sizeof(maintainer) - 1); else { struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_gecos && strlen(pw->pw_gecos)) strncpy(maintainer, pw->pw_gecos, sizeof(maintainer) - 1); else if (pw && pw->pw_name) strncpy(maintainer, pw->pw_name, sizeof(maintainer) - 1); else maintainer[0] = 0; } if ((cfg->query_maintainer == 1) || ((cfg->query_maintainer > 1) && (m == NULL))) { WINDOW *w = newwin(5, 52, LINES/2-3, (COLS-52)/2); box(w,0,0); enableInput(); wattron(w, uicolors[UI_COLOR_QUERY]); mvwaddstr(w, 1, 2, _("Maintainer name:")); wattroff(w, uicolors[UI_COLOR_QUERY]); wmove(w, 3, 2); wattron(w, uicolors[UI_COLOR_INPUT]); int i; for(i = strlen(maintainer)-1; i>=0; i--) ungetch(maintainer[i]); wgetnstr(w, maintainer, sizeof(maintainer)); disableInput(); wattroff(w, uicolors[UI_COLOR_INPUT]); delwin(w); refreshDraw(); } } gboolean ctrlKeyUpDown(int ic) { gboolean ret = FALSE; GList *dl; guint i = 0; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); switch (ic) { case KEY_UP: case 'k': if(!g_list_previous(dl)) return(ret); else { setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_previous(dl); setEntryActiveStatus((DrawNode *) dl->data, TRUE); if(((DrawNode *) dl->data)->scrpos == 0) { ((DrawNode *) dl->data)->scrpos = 1; reorderScrpos(1); } ret = TRUE; } break; case KEY_DOWN: case 'j': if(!g_list_next(dl)) return(ret); else { setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_next(dl); setEntryActiveStatus((DrawNode *) dl->data, TRUE); if(((DrawNode *) dl->data)->scrpos >= bottomDrawLine || ((DrawNode *) dl->data)->scrpos == 0) reorderScrpos(2); ret = TRUE; } break; case KEY_HOME: setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_first(drawlist); setEntryActiveStatus((DrawNode *) dl->data, TRUE); ((DrawNode *) dl->data)->scrpos = 1; reorderScrpos(1); ret = TRUE; break; case KEY_END : setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_last(drawlist); setEntryActiveStatus((DrawNode *) dl->data, TRUE); if(((DrawNode *) dl->data)->scrpos == 0 || ((DrawNode *) dl->data)->scrpos > bottomDrawLine) { i = 0; while (dl) { ((DrawNode *) dl->data)->scrpos = LINES-3-i; i++; i = i > LINES-3 ? LINES-3 : i; dl = g_list_previous(dl); } } ret = TRUE; break; default: break; } return (ret); } gboolean compDrawNodes(DrawNode* n1, DrawNode* n2) { if (n1->type != n2->type) return(FALSE); switch(n1->type) { case CATEGORY: case GROUP: if(!g_ascii_strcasecmp(n1->p, n2->p)) return(TRUE); break; case HOST: if (!g_ascii_strcasecmp(((HostNode *)(n1->p))->hostname, ((HostNode *)(n2->p))->hostname)) return(TRUE); break; case PKG: if (!g_ascii_strcasecmp(((PkgNode *) n1->p)->package, ((PkgNode *) n2->p)->package)) return(TRUE); break; case SESSION: if (((SessNode *) n1->p)->pid == ((SessNode *) n2->p)->pid) return(TRUE); break; default: break; } return(FALSE); } void rebuildDrawList(GList *hosts) { GList *dl_new = NULL; GList *dl_old = NULL; GList *old_drawlist = NULL; DrawNode *n_old = NULL; DrawNode *n_new = NULL; guint i=0, j=0, k=0, savesel=0; gboolean posset = FALSE, selset = FALSE; rebuilddl = FALSE; old_drawlist = drawlist; drawlist = NULL; buildIntialDrawList(hosts); dl_old = g_list_nth(old_drawlist,i); dl_new = g_list_nth(drawlist,j); while(dl_old && dl_new) { n_old = (DrawNode *) dl_old->data; n_new = (DrawNode *) dl_new->data; if (!n_old || !n_new) break; setEntryActiveStatus(n_new, FALSE); if (j==0) n_new->scrpos=0; if(compDrawNodes(n_new,n_old)) { if(!posset && n_old->scrpos == 1) { n_new->scrpos=1; posset=TRUE; } if(!selset && n_old->selected) { savesel = j; selset = TRUE; } if(n_old->extended == TRUE ) { setEntryActiveStatus(n_new, TRUE); detectPos(); ctrlKeyEnter(hosts); dl_new = g_list_nth(drawlist,j); n_new = (DrawNode *) dl_new->data; setEntryActiveStatus(n_new, FALSE); } i++; j++; dl_old = g_list_nth(old_drawlist,i); dl_new = g_list_nth(drawlist,j); } else { k=0; while (n_new->type >= n_old->type) { k++; dl_new = g_list_nth(drawlist,j+k); if (!dl_new) break; n_new = (DrawNode *) dl_new->data; if(n_new && compDrawNodes(n_old, n_new)) { if(!posset && n_old->scrpos==1) { n_new->scrpos=1; posset = TRUE; } if(!selset && n_old->selected) { savesel = j+k; selset = TRUE; } if(n_old->extended == TRUE ) { setEntryActiveStatus(n_new, TRUE); detectPos(); ctrlKeyEnter(hosts); dl_new = g_list_nth(drawlist,j+k); n_new = (DrawNode *) dl_new->data; setEntryActiveStatus(n_new, FALSE); } j+= k + 1; break; } } if (!posset && n_old->scrpos == 1) { dl_new = g_list_nth(drawlist,j); if (dl_new) { n_new = (DrawNode *) dl_new->data; if (n_new) { n_new->scrpos = 1; posset = TRUE; } } } if (!selset && n_old->selected) { savesel = j; selset = TRUE; } i++; dl_old = g_list_nth(old_drawlist,i); dl_new = g_list_nth(drawlist,j); } } dl_new = g_list_nth(drawlist,savesel); if (dl_new) { n_new = (DrawNode *) dl_new->data; if (n_new) setEntryActiveStatus(n_new, TRUE); } reorderScrpos(1); if(drawlist) freeDl(old_drawlist); else drawlist = old_drawlist; } void extDrawListCategory(gint atpos, gchar *category, GList *hosts) { guint i = 0; GList *ho; DrawNode *drawnode = NULL; DrawNode *parent = NULL; while(*(drawCategories+i)) { if(category == *(drawCategories+i)) break; i++; } if(!(*(drawCategories+i))) return; parent = g_list_nth_data(drawlist, (guint) atpos); ho = g_list_first(hosts); while(ho) { if( #ifdef FEAT_TCLFILTER (i == C_FILTERED) || #endif (((HostNode *) ho->data)->category == i) ) { if((drawnode) && (!g_ascii_strcasecmp(drawnode->p, ((HostNode *) ho->data)->group))) { if(((HostNode *) ho->data)->tagged == TRUE) drawnode->etagged++; ho = g_list_next(ho); continue; } gint elements = getHostGrpCatCnt(hosts, ((HostNode *) ho->data)->group, i); if(elements) { drawnode = g_new0(DrawNode, 1); #ifndef NDEBUG drawnode->_type = T_DRAWNODE; #endif drawnode->p = (((HostNode *) ho->data)->group); drawnode->type = GROUP; drawnode->extended = FALSE; drawnode->selected = FALSE; drawnode->scrpos = 0; drawnode->elements = elements; drawnode->attrs = A_NORMAL; drawnode->parent = parent; if(((HostNode *) ho->data)->tagged == TRUE) drawnode->etagged++; drawlist = g_list_insert(drawlist, drawnode, ++atpos); } } ho = g_list_next(ho); } reorderScrpos(1); } void extDrawListGroup(gint atpos, gchar *group, GList *hosts) { GList *ho; DrawNode *drawnode = NULL; DrawNode *parent = NULL; ho = g_list_first(hosts); parent = g_list_nth_data(drawlist, (guint) atpos); while(ho) { if(!g_ascii_strcasecmp(((HostNode *) ho->data)->group, group) && ( #ifdef FEAT_TCLFILTER (incategory == drawCategories[C_FILTERED] && ((HostNode *) ho->data)->filtered) || #endif (drawCategories[((HostNode *) ho->data)->category] == incategory) )) { drawnode = g_new0(DrawNode, 1); #ifndef NDEBUG drawnode->_type = T_DRAWNODE; #endif drawnode->p = ((HostNode *) ho->data); drawnode->type = HOST; drawnode->extended = FALSE; drawnode->selected = FALSE; drawnode->scrpos = 0; drawnode->parent = parent; if (((HostNode *) ho->data)->category != C_SESSIONS) drawnode->elements = ((HostNode *) ho->data)->nupdates + ((HostNode *) ho->data)->nholds + ((HostNode *) ho->data)->nextras + ((HostNode *) ho->data)->nbrokens; else drawnode->elements = g_list_length(((HostNode *) ho->data)->screens); drawnode->attrs = A_NORMAL; drawlist = g_list_insert(drawlist, drawnode, ++atpos); } ho = g_list_next(ho); } reorderScrpos(1); } void extDrawListHost(gint atpos, HostNode *n) { DrawNode *drawnode = NULL; DrawNode *parent = NULL; parent = g_list_nth_data(drawlist, (guint) atpos); ASSERT_TYPE(parent, T_DRAWNODE); if (n->category == C_SESSIONS) { GList *sess = g_list_first(n->screens); while(sess) { drawnode = g_new0(DrawNode, 1); #ifndef NDEBUG drawnode->_type = T_DRAWNODE; #endif drawnode->p = ((SessNode *)sess->data); drawnode->type = SESSION; drawnode->parent = parent; drawlist = g_list_insert(drawlist, drawnode, ++atpos); sess = g_list_next(sess); } reorderScrpos(1); return; } GList *upd = g_list_first(n->packages); while(upd) { if((((PkgNode *) upd->data)->flag & HOST_STATUS_PKGUPDATE) || (((PkgNode *) upd->data)->flag & HOST_STATUS_PKGKEPTBACK) || (((PkgNode *) upd->data)->flag & HOST_STATUS_PKGEXTRA) || (((PkgNode *) upd->data)->flag & HOST_STATUS_PKGBROKEN)) { drawnode = g_new0(DrawNode, 1); #ifndef NDEBUG drawnode->_type = T_DRAWNODE; #endif drawnode->type = PKG; drawnode->p = ((PkgNode *) upd->data); drawnode->attrs = (((PkgNode *) upd->data)->flag & HOST_STATUS_PKGUPDATE) ? A_BOLD : A_NORMAL; drawnode->parent = parent; drawlist = g_list_insert(drawlist, drawnode, ++atpos); } upd = g_list_next(upd); } reorderScrpos(1); } void shrinkDrawListCategory(gint atpos) { GList *dl; DrawNode *drawnode = NULL; dl = g_list_nth(drawlist, (guint) atpos+1); while(dl) { drawnode = ((DrawNode *) dl->data); if(drawnode->type != CATEGORY) { drawlist = g_list_remove(drawlist, drawnode); freeDrawNode(drawnode); } else break; dl = g_list_nth(drawlist, (guint) atpos+1); } reorderScrpos(1); } void shrinkDrawListGroup(gint atpos) { GList *dl; DrawNode *drawnode = NULL; dl = g_list_nth(drawlist, (guint) atpos+1); while(dl) { drawnode = ((DrawNode *) dl->data); if(drawnode->type != GROUP && drawnode->type != CATEGORY) { drawlist = g_list_remove(drawlist, drawnode); freeDrawNode(drawnode); } else break; dl = g_list_nth(drawlist, (guint) atpos+1); } reorderScrpos(1); } void shrinkDrawListHost(gint atpos) { GList *dl; DrawNode *drawnode = NULL; dl = g_list_nth(drawlist, (guint) atpos+1); while(dl) { drawnode = ((DrawNode *) dl->data); if(drawnode->type != GROUP && drawnode->type != CATEGORY && drawnode->type != HOST) { drawlist = g_list_remove(drawlist, drawnode); freeDrawNode(drawnode); } else break; dl = g_list_nth(drawlist, (guint) atpos+1); } reorderScrpos(1); } void extDrawList(gint atpos, gboolean extend, DrawNode *atn, GList *hosts) { if(!atn) return; if(extend == TRUE) { switch(atn->type) { case CATEGORY: extDrawListCategory(atpos, (gchar *) atn->p, hosts); break; case GROUP: extDrawListGroup(atpos, (gchar *) atn->p, hosts); break; case HOST: extDrawListHost(atpos, (HostNode *) atn->p); break; default: break; } } else { switch(atn->type) { case CATEGORY: shrinkDrawListCategory(atpos); break; case GROUP: shrinkDrawListGroup(atpos); break; case HOST: shrinkDrawListHost(atpos); break; default: break; } } } gboolean ctrlKeyEnter(GList *hosts) { gboolean ret = TRUE; gint extpos = 0; GList *dl; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) { extpos++; dl = g_list_next(dl); } if (((DrawNode *) dl->data)->elements == 0) return (ret); ((DrawNode *) dl->data)->extended = ((DrawNode *) dl->data)->extended == TRUE ? FALSE : TRUE; extDrawList(extpos, ((DrawNode *) dl->data)->extended,(DrawNode *) dl->data, hosts); return (ret); } gboolean ctrlKeyLeft(GList *hosts) { gboolean ret = TRUE; GList *dl, *dl_1; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); if (((DrawNode *) dl->data)->extended) ret = ctrlKeyEnter(hosts); else if (((DrawNode *) dl->data)->type > 0){ setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl_1 = dl; while (dl && (((DrawNode *) dl->data)->type == ((DrawNode *) dl_1->data)->type)) dl = g_list_previous(dl); if (dl) { setEntryActiveStatus((DrawNode *) dl->data, TRUE); if(((DrawNode *) dl->data)->scrpos == 0) { ((DrawNode *) dl->data)->scrpos = 1; reorderScrpos(1); } } else setEntryActiveStatus((DrawNode *) dl_1->data, TRUE); } else { setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_first(drawlist); if (dl) { ((DrawNode *) dl->data)->scrpos = 1; setEntryActiveStatus((DrawNode *) dl->data, TRUE); reorderScrpos(1); } } return (ret); } gboolean ctrlKeyRight(GList *hosts) { gboolean ret = TRUE; GList *dl; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); if (!((DrawNode *) dl->data)->extended) ret = ctrlKeyEnter(hosts); return (ret); } gboolean ctrlKeyPgDown() { gboolean ret = TRUE; GList *dl; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); setEntryActiveStatus((DrawNode *) dl->data, FALSE); reorderScrpos(bottomDrawLine); dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->scrpos != 1)) dl = g_list_next(dl); if(dl) setEntryActiveStatus((DrawNode *) dl->data, TRUE); else { dl = g_list_last(drawlist); if(dl) { setEntryActiveStatus((DrawNode *) dl->data, TRUE); ((DrawNode *) dl->data)->scrpos =1; } } return (ret); } gboolean ctrlKeyPgUp() { gboolean ret = TRUE; gint i = 0; GList *dl, *dl_1; dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->selected != TRUE)) dl = g_list_next(dl); setEntryActiveStatus((DrawNode *) dl->data, FALSE); dl = g_list_first(drawlist); while (dl && (((DrawNode *) dl->data)->scrpos != 1)) dl = g_list_next(dl); dl_1 = dl; dl_1 = g_list_previous(dl_1); while (dl && (++i < bottomDrawLine)) dl = g_list_previous(dl); if (dl && dl_1) { ((DrawNode *) dl->data)->scrpos =1; setEntryActiveStatus((DrawNode *) dl_1->data, TRUE); } else { dl = g_list_first(drawlist); if(dl) { ((DrawNode *) dl->data)->scrpos =1; setEntryActiveStatus((DrawNode *) dl->data, TRUE); } } reorderScrpos(1); return (ret); } void injectKey(int ch) { ungetch(ch); } static void expandAllNodes(GList *hosts) { gint i; GList *dl = NULL; /* UGLY: expand everything (so the matched host is on the drawlist) */ for(i=2; i>0; i--) { dl = g_list_first(drawlist); while(dl) { DrawNode *dn = (DrawNode *) dl->data; if(dn->type != HOST) dn->extended = TRUE; dl = g_list_next(dl); } rebuildDrawList(hosts); } completion_set_entries(dlCompl, drawlist); } void searchEntry(GList *hosts) { gint c; gchar s[BUF_MAX_LEN]; gint pos = 0; const gchar *query = _("Search: "); const int offset = strlen(query)-1; GList *matches = NULL; GList *selmatch = NULL; GList *dl = NULL; GList *dlkeep = NULL; enableInput(); noecho(); memset(s, 0, sizeof(s)); drawQuery(query, 0); while((c = getch())) { /* handle backspace */ if(c == KEY_BACKSPACE) { if (strlen(s)>0) { s[--pos] = 0; if(strlen(s)>0) expandAllNodes(hosts); selmatch = NULL; } else beep(); } /* search again */ else if((c == '/') || (c == '\t')) { if(selmatch && matches) { selmatch = g_list_next(selmatch); if(!selmatch) selmatch = g_list_first(matches); } if(!selmatch) beep(); } /* abort on control key */ else if(iscntrl(c) || c==KEY_LEFT || c==KEY_DOWN) { if((c==KEY_UP) || (c==KEY_DOWN)) ungetch(c); break; } /* accept char */ else if(strlen(s) 0)) { beep(); s[--pos] = 0; if(pos>0) matches = completion_search(dlCompl, s); } /* print new search string */ else { attron(uicolors[UI_COLOR_INPUT]); mvaddstr(LINES-1, offset+1, s); attroff(uicolors[UI_COLOR_INPUT]); } /* check if selected match is still valid... or get new one */ if(matches) { if(selmatch) { if(!g_list_find(matches, selmatch->data)) selmatch = g_list_first(matches); } else selmatch = g_list_first(matches); if(selmatch) { attron(uicolors[UI_COLOR_INPUT]); attron(A_REVERSE); gchar *selstr = getStrFromDrawNode((DrawNode *) selmatch->data); mvaddstr(LINES-1, offset+pos+1, &selstr[pos]); attroff(A_REVERSE); attroff(uicolors[UI_COLOR_INPUT]); remln(COLS-offset-strlen(&selstr[pos])-1); } else remln(COLS-offset-pos-1); /* we have a match which is selected */ if(selmatch) { /* Shrink all drawnodes which are not matches. */ GList *m = g_list_first(matches); while(m) { DrawNode *parent = ((DrawNode *) m->data)->parent; while(parent) { /* Keep the following drawnodes extended. */ dlkeep = g_list_append(dlkeep, parent); parent = parent->parent; } m = g_list_next(m); } dl = g_list_last(drawlist); while(dl) { DrawNode *dn = (DrawNode *) dl->data; if(dn->extended == TRUE) { GList *k = g_list_first(dlkeep); while(k) { if(k->data == dn) break; k = g_list_next(k); } /* Shrink the list, if is not found in the search list. */ if(!k) { dn->extended = FALSE; extDrawList(g_list_index(drawlist, dn), FALSE, dn, hosts); } } dl = g_list_previous(dl); } if(dlkeep) { g_list_free(dlkeep); dlkeep = NULL; } /* clear selection */ DrawNode *n = getSelectedDrawNode(); if (n) setEntryActiveStatus(n, FALSE); /* traverse drawlist bottom up... and only expand * the path to the selmatch */ dl = g_list_last(drawlist); while(dl) { DrawNode *dn = (DrawNode *) dl->data; dn->scrpos = 0; if(selmatch->data == dn) { setEntryActiveStatus(dn, TRUE); dn->scrpos = 1; reorderScrpos(1); break; } dl = g_list_previous(dl); } cleanBetween(); g_list_foreach(drawlist, (GFunc) drawEntry, NULL); } } else remln(COLS-offset-pos-1); /* print matches in status bar */ if(matches) { gint dssize = COLS+1; gchar *dsstr = NULL; dsstr = g_malloc0(dssize); if(!dsstr) break; g_strlcat(dsstr, _("Matches:"), dssize); GList *m = g_list_first(matches); while(m) { g_strlcat(dsstr, " ", dssize); DrawNode *dn = (DrawNode *) m->data; g_strlcat(dsstr, getStrFromDrawNode(dn), dssize); m = g_list_next(m); } drawStatus(dsstr, FALSE); g_free(dsstr); } else { drawStatus(_("Matches: -"), FALSE); selmatch = NULL; } /* move to cursor position */ move(LINES-1, offset+pos+1); refresh(); } attroff(uicolors[UI_COLOR_INPUT]); disableInput(); move(LINES - 1, 0); remln(COLS); drawStatus ("", TRUE); } #ifdef FEAT_TCLFILTER void applyFilter(GList *hosts) { if(!strlen(filterexp)) return; gint i; gboolean filtered; GList *l = hosts; Tcl_Preserve(tcl_interp); while(l) { HostNode *n = (HostNode *)l->data; for(i=0; tclmap[i].name; i++) { switch(tclmap[i].type) { case TCLM_STRING: switch(tclmap[i].code) { case TCLMK_GROUP: Tcl_SetVar(tcl_interp, tclmap[i].name, n->group, 0); break; case TCLMK_HOSTNAME: Tcl_SetVar(tcl_interp, tclmap[i].name, n->hostname, 0); break; case TCLMK_COMMENT: Tcl_SetVar(tcl_interp, tclmap[i].name, n->comment, 0); break; case TCLMK_KERNEL: Tcl_SetVar(tcl_interp, tclmap[i].name, n->kernelrel, 0); break; case TCLMK_LSBCNAME: Tcl_SetVar(tcl_interp, tclmap[i].name, n->lsb_codename, 0); break; case TCLMK_LSBDISTRI: Tcl_SetVar(tcl_interp, tclmap[i].name, n->lsb_distributor, 0); break; case TCLMK_LSBREL: Tcl_SetVar(tcl_interp, tclmap[i].name, n->lsb_release, 0); break; case TCLMK_UNKERNEL: Tcl_SetVar(tcl_interp, tclmap[i].name, n->uname_kernel, 0); break; case TCLMK_UNMACHINE: Tcl_SetVar(tcl_interp, tclmap[i].name, n->uname_machine, 0); break; case TCLMK_VIRT: Tcl_SetVar(tcl_interp, tclmap[i].name, n->virt, 0); break; default: g_warning(_("Internal error: unhandled TCL TCLM_STRING mapping!")); } break; case TCLM_INT: { gchar *h = NULL; switch(tclmap[i].code) { case TCLMK_CATEGORY: h = g_strdup_printf("%d", n->category); break; case TCLMK_FORBID: h = g_strdup_printf("%d", n->forbid); break; default: g_warning(_("Internal error: unhandled TCL TCLM_INT mapping!")); } Tcl_SetVar(tcl_interp, tclmap[i].name, h, 0); g_free(h); } break; case TCLM_IGNORE: break; default: g_warning(_("Internal error: unknown TCL mapping type!")); } } Tcl_UnsetVar(tcl_interp, "updates", 0); Tcl_UnsetVar(tcl_interp, "installed", 0); Tcl_UnsetVar(tcl_interp, "extras", 0); Tcl_UnsetVar(tcl_interp, "holds", 0); GList *p = n->packages; while(p) { Tcl_SetVar2(tcl_interp, "installed", ((PkgNode *)(p->data))->package, ((PkgNode *)(p->data))->version, 0); if( ((PkgNode *)p->data)->flag & HOST_STATUS_PKGUPDATE) Tcl_SetVar2(tcl_interp, "updates", ((PkgNode *)(p->data))->package, ((PkgNode *)(p->data))->data, 0); if( ((PkgNode *)p->data)->flag & HOST_STATUS_PKGKEPTBACK) Tcl_SetVar2(tcl_interp, "holds", ((PkgNode *)(p->data))->package, ((PkgNode *)(p->data))->data, 0); if( ((PkgNode *)p->data)->flag & HOST_STATUS_PKGEXTRA) Tcl_SetVar2(tcl_interp, "extras", ((PkgNode *)(p->data))->package, ((PkgNode *)(p->data))->version, 0); p = g_list_next(p); } Tcl_UnsetVar(tcl_interp, "flags", 0); for(i=0; hostFlags[i].code; i++) { if(n->status & hostFlags[i].flag) Tcl_SetVar2(tcl_interp, "flags", hostFlags[i].code, hostFlags[i].code, 0); } #ifdef FEAT_CLUSTERS Tcl_UnsetVar(tcl_interp, "clusters", 0); GList *c = n->clusters; while(c) { Tcl_SetVar2(tcl_interp, "clusters", c->data, c->data, 0); c = g_list_next(c); } #endif Tcl_ResetResult(tcl_interp); switch(Tcl_Eval(tcl_interp, filterexp)) { case TCL_OK: case TCL_RETURN: filtered = atoi(Tcl_GetStringResult(tcl_interp)) > 0; break; default: filtered = FALSE; break; } if(filtered != n->filtered) { n->filtered = filtered; rebuilddl = TRUE; } l = g_list_next(l); } /* while */ Tcl_Release(tcl_interp); } static void filterHosts(GList *hosts) { gint i, r; gint first; WINDOW *w = newwin(LINES-3, COLS, 2, 0); wattron(w, uicolors[UI_COLOR_QUERY]); mvwaddstr(w, 1, 0, _("Scalars:")); wattroff(w, uicolors[UI_COLOR_QUERY]); waddstr(w, "\n"); first = 1; for(i=0; tclmap[i].name; i++) { if((tclmap[i].type != TCLM_STRING) && (tclmap[i].type != TCLM_INT)) continue; if(!first) waddstr(w, ", "); else first = 0; waddstr(w, tclmap[i].name); } waddstr(w, "\n\n"); wattron(w, uicolors[UI_COLOR_QUERY]); waddstr(w, _("Arrays:")); wattroff(w, uicolors[UI_COLOR_QUERY]); waddstr(w, "\n"); first = 1; for(i=0; tclmap[i].name; i++) { if(tclmap[i].type != TCLM_IGNORE) continue; if(!first) waddstr(w, ", "); else first = 0; waddstr(w, tclmap[i].name); } waddstr(w, "\n\n"); wattron(w, uicolors[UI_COLOR_QUERY]); waddstr(w, _("Examples:")); wattroff(w, uicolors[UI_COLOR_QUERY]); waddstr(w, "\n"); waddstr(w, "return [expr [string compare $lsb_distri \"Debian\"] == 0]\n"); waddstr(w, "return [expr [string compare $lsb_distri \"Debian\"] == 0 && $lsb_rel < 4.0]\n"); waddstr(w, "return [llength [array names installed \"bind*\"]]\n"); waddstr(w, "return [expr [string compare $virt \"Xen\"] == 0]\n"); waddstr(w, "\n"); wattron(w, uicolors[UI_COLOR_QUERY]); waddstr(w, _("Enter filter expression:")); wattroff(w, uicolors[UI_COLOR_QUERY]); waddstr(w, "\n"); enableInput(); wattron(w, uicolors[UI_COLOR_INPUT]); for(i = strlen(filterexp)-1; i>=0; i--) ungetch(filterexp[i]); r = getnLine(w, filterexp, sizeof(filterexp)-1, FALSE); disableInput(); wattroff(w, uicolors[UI_COLOR_INPUT]); delwin(w); refreshDraw(); if(r) applyFilter(hosts); } #endif #ifdef FEAT_HISTORY static void handleErrors(HostNode *n) { int c; HistoryEntry *he = history_recent_entry(cfg, n); if(!he) return; gchar *query; if(HOST_SSHPORT_SET(n)) query = g_strdup_printf(_("An error at %s:%d has been detected [Less/ignore/connect]: "), n->hostname, n->ssh_port); else query = g_strdup_printf(_("An error at %s has been detected [Less/ignore/connect]: "), n->hostname); notifyUser(); while(1) { enableInput(); drawQuery(query, 0); move(LINES - 1, strlen(query)); attron(uicolors[UI_COLOR_INPUT]); c = getch(); attroff(uicolors[UI_COLOR_INPUT]); disableInput(); move(LINES - 1, 0); remln(COLS); switch(c) { /* ignore */ case 27: case 'i': case 'I': case 'q': case 'Q': history_free_he(he); return; /* connect */ case 'c': case 'C': history_free_he(he); ignoreSIGINT(TRUE); endwin(); ssh_connect(n, FALSE); ignoreSIGINT(FALSE); refresh(); return; default: ignoreSIGINT(TRUE); endwin(); history_show_less_search(he, cfg->history_errpattern); ignoreSIGINT(FALSE); refresh(); break; } } history_free_he(he); } #endif gboolean ctrlUI (GList *hosts) { gint hostcnt; int ic, i; gboolean ret = TRUE; gboolean retqry = FALSE; gboolean refscr = FALSE; DrawNode *n; static gchar in[INPUT_MAX], in2[INPUT_MAX]; static gboolean keytagactive = FALSE; gchar *qrystr = NULL; gchar *pkg = NULL; guint sc = SC_MAX; if(rebuilddl == TRUE) { rebuildDrawList(hosts); refscr = TRUE; } if((ic = getch()) == -1) refresh(); else for(i = 0; shortCuts[i].keycode; i++) if(shortCuts[i].keycode == ic) sc = shortCuts[i].sc; #ifdef KEY_RESIZE if(ic == KEY_RESIZE) { refscr = TRUE; dump_screen = FALSE; bottomDrawLine = LINES-2; clear(); } #endif if(sc != SC_MAX) { if(keytagactive == TRUE) sc+=TAGGED_MASK; switch(sc) { case SC_KEY_HOME: case SC_KEY_END: case SC_KEY_UP: case SC_KEY_DOWN: case SC_KEY_UP2: case SC_KEY_DOWN2: refscr = ctrlKeyUpDown(ic); break; /* case SC_KEY_DOWN */ case SC_KEY_PPAGE: refscr = ctrlKeyPgUp(); break; /* case SC_KEY_PPAGE */ case SC_KEY_NPAGE: refscr = ctrlKeyPgDown(); break; /* case SC_KEY_NPAGE */ case SC_KEY_LEFT: case SC_KEY_LEFT2: refscr = ctrlKeyLeft(hosts); break; /* case SC_KEY_LEFT */ case SC_KEY_RIGHT: case SC_KEY_RIGHT2: refscr = ctrlKeyRight(hosts); break; /* case SC_KEY_RIGHT */ case SC_KEY_PLUS: case SC_KEY_SPACE: case SC_KEY_ENTER: case SC_KEY_RETURN: refscr = ctrlKeyEnter(hosts); break; /* case SC_KEY_RETURN */ case SC_KEY_REFRESH: n = getSelectedDrawNode(); if(!n) break; if(n->type == HOST) { if(n->extended == TRUE) n->extended = FALSE; ((HostNode *) n->p)->category = C_REFRESH_REQUIRED; rebuildDrawList(hosts); refscr = TRUE; } else if(n->type == GROUP) { if(n->extended == TRUE) n->extended = FALSE; if(drawCategories[getCategoryNumber(incategory)]) { setHostsCategory(hosts, getCategoryNumber(incategory), ingroup, (Category) C_REFRESH_REQUIRED); rebuildDrawList(hosts); refscr = TRUE; } } else if(n->type == CATEGORY) { if(n->extended == TRUE) n->extended = FALSE; if(drawCategories[getCategoryNumber(incategory)]) { setHostsCategory(hosts, getCategoryNumber(incategory), NULL, (Category) C_REFRESH_REQUIRED); rebuildDrawList(hosts); refscr = TRUE; } } #ifdef FEAT_TMUX refreshStats(hosts); #endif break; /* case SC_KEY_REFRESH */ case SC_KEY_REFRESH+TAGGED_MASK: { GList *thosts = NULL; GList *ho = g_list_first(hosts); while(ho) { HostNode *m = (HostNode *)ho->data; if(m->tagged == TRUE) thosts = g_list_prepend(thosts, m); ho = g_list_next(ho); } if(thosts && g_list_length (thosts) > 0) { qrystr = g_strdup_printf(_("Refresh %d tagged hosts? [y/N]: "), g_list_length (thosts)); retqry = queryConfirm(qrystr, FALSE, NULL); g_free(qrystr); if(retqry == TRUE) { GList *ho = g_list_first(thosts); while(ho) { HostNode *m = (HostNode *)ho->data; m->category = C_REFRESH_REQUIRED; refscr = TRUE; ho = g_list_next(ho); } if(refscr == TRUE) rebuildDrawList(hosts); } g_list_free (thosts); } } #ifdef FEAT_TMUX refreshStats(hosts); #endif break; /* case SC_KEY_REFRESH + KEY_TAGGED_MASK */ case SC_KEY_TAGACTION: { GList *ho = g_list_first(hosts); while(ho) { if(((HostNode *)ho->data)->tagged == TRUE) break; ho = g_list_next(ho); } if(ho) { keytagactive = !keytagactive; if(keytagactive == TRUE) { drawQuery(_("tag-"), 0); curs_set(1); } } else beep(); } /* case SC_KEY_TAGACTION */ break; case SC_KEY_CONNECT: n = getSelectedDrawNode(); if(!n) break; if(n->type == HOST) { if(n->extended == TRUE) n->extended = FALSE; if (g_list_length(inhost->screens)) { if (!queryConfirm(_("There are running sessions on this host! Continue? [y/N]: "), FALSE, NULL)) break; } dump_screen = FALSE; cleanUI(); ssh_connect((HostNode *) n->p, FALSE); ((HostNode *) n->p)->category = C_REFRESH_REQUIRED; rebuildDrawList(hosts); initUI(); refscr = TRUE; } break; /* case SC_KEY_CONNECT */ case SC_KEY_UPGRADE: n = getSelectedDrawNode(); if(!n) break; switch(n->type) { case HOST: if(inhost->forbid & HOST_FORBID_UPGRADE) break; if(n->extended == TRUE) n->extended = FALSE; if (g_list_length(inhost->screens)) { if (!queryConfirm(_("There are running sessions on this host! Continue? [y/N]: "), FALSE, NULL)) break; } cleanUI(); #ifdef FEAT_HISTORY gboolean f = #endif ssh_cmd_upgrade((HostNode *) n->p, FALSE); #ifdef FEAT_HISTORY if(f) handleErrors((HostNode *) n->p); #endif ((HostNode *) n->p)->category = C_REFRESH_REQUIRED; rebuildDrawList(hosts); initUI(); refscr = TRUE; #ifdef FEAT_TMUX refreshStats(hosts); #endif break; case CATEGORY: case GROUP: default: { if(((n->type == CATEGORY) && !queryConfirm(_("Run update for the whole category? [y/N]: "), FALSE, NULL)) || ((n->type == GROUP) && !queryConfirm(_("Run update for the whole group? [y/N]: "), FALSE, NULL))) break; GList *ho = g_list_first(hosts); gint cat = getCategoryNumber(incategory); while(ho) { HostNode *m = (HostNode *)ho->data; #ifdef FEAT_TCLFILTER if((n->type == GROUP && (strcmp(m->group, ingroup) == 0) && ((cat == C_FILTERED && m->filtered == TRUE) || m->category == cat)) || (n->type == CATEGORY && ((cat == C_FILTERED && m->filtered == TRUE) || m->category == cat))) { #else if((n->type == GROUP && (strcmp(m->group, ingroup) == 0) && m->category == cat) || (n->type == CATEGORY && m->category == cat)) { #endif if(m->forbid ^ HOST_FORBID_UPGRADE) ssh_cmd_upgrade(m, TRUE); } ho = g_list_next(ho); } } break; } break; /* case SC_KEY_UPGRADE */ case SC_KEY_UPGRADE + TAGGED_MASK: { GList *thosts = NULL; GList *ho = g_list_first(hosts); while(ho) { HostNode *m = (HostNode *)ho->data; if(m->forbid ^ HOST_FORBID_UPGRADE && m->nupdates > 0 && m->tagged == TRUE) thosts = g_list_prepend(thosts, m); ho = g_list_next(ho); } if(thosts && g_list_length (thosts) > 0) { qrystr = g_strdup_printf(_("Run update for %d tagged and updatable hosts? [y/N]: "), g_list_length (thosts)); retqry = queryConfirm(qrystr, FALSE, NULL); g_free(qrystr); if(retqry == TRUE) { GList *ho = g_list_first(thosts); while(ho) { HostNode *m = (HostNode *)ho->data; ssh_cmd_upgrade(m, TRUE); ho = g_list_next(ho); } } g_list_free (thosts); } else beep(); } break; /* case SC_KEY_UPGRADE + TAGGED_MASK */ case SC_KEY_INSTALL: n = getSelectedDrawNode(); if(!n) break; switch(n->type) { case PKG: if(n->p && (inhost->forbid ^ HOST_FORBID_INSTALL)) { pkg = ((PkgNode *) n->p)->package; if(((PkgNode *) n->p)->flag & HOST_STATUS_PKGUPDATE) { qrystr = g_strdup_printf(_("Install package `%s' [y/N]: "), pkg); if(!qrystr) break; retqry = queryConfirm(qrystr, FALSE, NULL); g_free(qrystr); qrystr = NULL; } else { if(queryString(_("Install package: "), in, sizeof(in)-1) == FALSE) break; if (strlen(in)==0) break; pkg = in; retqry = TRUE; } if(retqry == TRUE) { cleanUI(); ssh_cmd_install(inhost, pkg, FALSE); inhost->category = C_REFRESH_REQUIRED; rebuildDrawList(hosts); initUI(); refscr = TRUE; } } break; case HOST: if(n->extended == TRUE) n->extended = FALSE; if(inhost->forbid & HOST_FORBID_INSTALL) break; if (g_list_length(inhost->screens)) { if (!queryConfirm(_("There are running sessions on this host! Continue? [y/N]: "), FALSE, NULL)) break; } if(queryString(_("Install package: "), in, sizeof(in)-1) == FALSE) break; if (strlen(in)==0) break; pkg = in; cleanUI(); ssh_cmd_install(inhost, pkg, FALSE); inhost->category = C_REFRESH_REQUIRED; rebuildDrawList(hosts); initUI(); refscr = TRUE; break; case CATEGORY: case GROUP: default: { if(queryString(_("Install package: "), in, sizeof(in)-1) == FALSE) break; if (strlen(in)==0) break; if(((n->type == CATEGORY) && !queryConfirm(_("Run install for the whole category? [y/N]: "), FALSE, NULL)) || ((n->type == GROUP) && !queryConfirm(_("Run install for the whole group? [y/N]: "), FALSE, NULL))) break; GList *ho = g_list_first(hosts); gint cat = getCategoryNumber(incategory); while(ho) { HostNode *m = (HostNode *)ho->data; #ifdef FEAT_TCLFILTER if((n->type == GROUP && (strcmp(m->group, ingroup) == 0) && ((cat == C_FILTERED && m->filtered == TRUE) || m->category == cat)) || (n->type == CATEGORY && ((cat == C_FILTERED && m->filtered == TRUE) || m->category == cat))) { #else if((n->type == GROUP && (strcmp(m->group, ingroup) == 0) && m->category == cat) || (n->type == CATEGORY && m->category == cat)) { #endif if(m->forbid ^ HOST_FORBID_INSTALL) ssh_cmd_install(m, in, TRUE); } ho = g_list_next(ho); } } break; } break; /* case SC_KEY_INSTALL */ case SC_KEY_INSTALL + TAGGED_MASK: { GList *thosts = NULL; GList *ho = g_list_first(hosts); while(ho) { HostNode *m = (HostNode *)ho->data; if(m->forbid ^ HOST_FORBID_INSTALL && m->tagged == TRUE) thosts = g_list_prepend(thosts, m); ho = g_list_next(ho); } if(thosts && g_list_length (thosts) > 0) { qrystr = g_strdup_printf(_("Install package on %d tagged hosts: "), g_list_length (thosts)); if(!qrystr) break; retqry = TRUE; if(queryString(qrystr, in, sizeof(in)-1) == FALSE) retqry = FALSE; if (strlen(in)==0) retqry = FALSE; g_free(qrystr); if(retqry == TRUE) { pkg = in; GList *ho = g_list_first(thosts); while(ho) { HostNode *m = (HostNode *)ho->data; ssh_cmd_install(m, pkg, TRUE); ho = g_list_next(ho); } } g_list_free (thosts); } else beep(); } break; /* case SC_KEY_INSTALL + TAGGED_MASK */ case SC_KEY_TAGMATCH: case SC_KEY_UNTAGMATCH: { if(queryString(sc == SC_KEY_TAGMATCH ? _("Tag hosts matching: ") : _("Untag hosts matching: "), in2, sizeof(in2)-1) == FALSE) break; if (strlen(in2)==0) break; GList *ho = g_list_first(hosts); while(ho) { HostNode *n = (HostNode *)ho->data; if(strlen(n->hostname) >= strlen(in2)) { if(compHostWithPattern (n, in2, strlen(in2)) == TRUE) { n->tagged= sc == SC_KEY_TAGMATCH ? TRUE : FALSE; refscr= TRUE; } } ho = g_list_next(ho); } if(refscr == FALSE) beep(); else { GList *dl = g_list_first(drawlist); while(dl) { DrawNode *drawnode = dl->data; if(drawnode->type == CATEGORY) { i=0; while(*(drawCategories+i)) { if(*(drawCategories+i) == drawnode->p) drawnode->etagged = getHostCatTaggedCnt(hosts, i); i++; } } else if(drawnode->type == GROUP) { i=0; while(*(drawCategories+i)) { if(*(drawCategories+i) == drawnode->parent->p) drawnode->etagged = getHostGrpCatTaggedCnt(hosts, drawnode->p, i); i++; } } dl = g_list_next(dl); } refscr = TRUE; } } break; /* case SC_KEY_TAGMATCH */ case SC_KEY_CYCLESESS: case SC_KEY_NEXTSESS: { /* Count the number of screens */ GList *ho = g_list_first(hosts); i=0; while(ho) { HostNode *m = (HostNode *)ho->data; GList *scr = m->screens; if(scr) i+=g_list_length (scr); ho = g_list_next(ho); } ho = g_list_first(hosts); while(ho) { gboolean cancel = FALSE; HostNode *m = (HostNode *)ho->data; GList *scr = m->screens; while(scr) { qrystr = g_strdup_printf(_("Attach host %s session %d (%d %s left) [Y/n/c]: "), m->hostname, ((SessNode *)scr->data)->pid, i, i == 1 ? _("session") : _("sessions")); i--; if(!qrystr) { g_warning(_("Out of memory.")); break; } retqry = queryConfirm(qrystr, TRUE, &cancel); g_free(qrystr); qrystr = NULL; if(cancel == TRUE) { ho = NULL; break; } if(retqry == FALSE) { scr = g_list_next(scr); continue; } if (!TTYMUX_IS_ATTACHED((SessNode *)scr->data)) { cleanUI(); #ifdef FEAT_HISTORY gboolean f = #endif TTYMUX_ATTACH(m, (SessNode *)scr->data, FALSE); #ifdef FEAT_HISTORY if(f) handleErrors(m); #endif initUI(); if(ic != getKeyForShortcut(sc)) { ho = NULL; break; } } scr = g_list_next(scr); } if(ho) ho = g_list_next(ho); } } #ifdef FEAT_TMUX refreshStats(hosts); #endif break; /* case SC_KEY_NEXTSESS */ case SC_KEY_ATTACH: n = getSelectedDrawNode(); if(!inhost) break; SessNode *s = NULL; if(!n || (n->type != SESSION)) { GList *l = g_list_first(inhost->screens); if (l) s = l->data; } else { s = (SessNode *) n->p; } if(!s) break; { gboolean may_share = FALSE; /* Session already attached! */ if (TTYMUX_IS_ATTACHED(s)) { if (!queryConfirm(_("Already attached - share session? [y/N]: "), FALSE, NULL)) break; may_share = TRUE; } dump_screen = FALSE; cleanUI(); #ifdef FEAT_HISTORY gboolean f = #endif TTYMUX_ATTACH(inhost, s, may_share); #ifdef FEAT_HISTORY if(f) handleErrors(inhost); #endif initUI(); } refscr = TRUE; #ifdef FEAT_TMUX refreshStats(hosts); #endif break; /* case SC_KEY_ATTACH */ case SC_KEY_TOGGLEDUMPS: if (!cfg->dump_screen) break; dump_screen = !dump_screen; if(getSelectedDrawNode()->type != SESSION) { if(dump_screen) drawQuery(_("Session dumps enabled."), G_USEC_PER_SEC/2); else drawQuery(_("Session dumps disabled."), G_USEC_PER_SEC/2); } else refscr = TRUE; break; /* case SC_KEY_TOGGLEDUMPS */ case SC_KEY_HELP: { drawHelp(); refscr = TRUE; } break; /* case SC_KEY_HELP */ case SC_KEY_MORE: if (inhost) { drawHostDetails(inhost); refscr = TRUE; } break; /* case SC_KEY_MORE */ case SC_KEY_ERRDIAG: if (inhost) { drawHostErrDiag(inhost); refscr = TRUE; } break; /* case SC_KEY_ERRDIAG */ #ifdef FEAT_HISTORY case SC_KEY_HISTORY: if (inhost) { drawHistoryEntries (inhost); refscr = TRUE; } break; /* case SC_KEY_HISTORY */ #endif case SC_KEY_FIND: searchEntry(hosts); refscr = TRUE; break; case SC_KEY_TAG: n = getSelectedDrawNode(); if(!n) break; if(n->type == HOST) { ((HostNode *) n->p)->tagged = !((HostNode *) n->p)->tagged; DrawNode *p = n->parent; while(p) { p->etagged+= ((HostNode *) n->p)->tagged == TRUE ? 1 : -1; drawEntry(p); p = p->parent; } drawEntry(n); } else beep(); break; #ifdef FEAT_TCLFILTER case SC_KEY_FILTER: filterHosts(hosts); rebuildDrawList(hosts); refscr = TRUE; break; #endif case SC_KEY_FILETRANS: n = getSelectedDrawNode(); if(!n) break; if(n->type == HOST) { if(n->extended == TRUE) n->extended = FALSE; cleanUI(); sftp_connect((HostNode *) n->p); initUI(); refscr = TRUE; } break; /* case SC_KEY_FILETRANS */ case SC_KEY_QUIT: hostcnt = 0; GList *ho = g_list_first(hosts); while(ho) { HostNode *m = (HostNode *)ho->data; if(m->category == C_REFRESH_REQUIRED || m->category == C_REFRESH) hostcnt++; ho = g_list_next(ho); } if(hostcnt > 0) { if (hostcnt != 1) { qrystr = g_strdup_printf(_("There are %d hosts in status refresh state, quit apt-dater? [y/N]: "), hostcnt); } else { qrystr = g_strdup_printf(_("There is %d host in status refresh state, quit apt-dater? [y/N]: "), hostcnt); } retqry = queryConfirm(qrystr, FALSE, NULL); g_free(qrystr); if (retqry == FALSE) break; } #ifdef FEAT_TCLFILTER if (!Tcl_InterpDeleted(tcl_interp)) Tcl_DeleteInterp(tcl_interp); #endif if(dlCompl) completion_free (dlCompl); ret = FALSE; attrset(A_NORMAL); cleanUI(); refreshUI(); refscr = FALSE; g_main_loop_quit (loop); break; /* case SC_KEY_QUIT */ #ifdef FEAT_RUNCUST case SC_KEY_RUNCUST: runCustom(); rebuildDrawList(hosts); refscr = TRUE; break; /* case SC_KEY_RUNCUST */ #endif default: break; } /* switch (sc) */ } if(((keytagactive == TRUE && sc != SC_KEY_TAGACTION) || (keytagactive == FALSE && SC_KEY_TAGACTION)) && ic != -1) { keytagactive = FALSE; refscr = TRUE; curs_set(0); remln(COLS); } if(refscr == TRUE) { getOldestMtime(hosts); refreshDraw(); } /* Redraw only the an session entry if screen dump enabled. */ if(dump_screen && (n = getSelectedDrawNode()) && n->type==SESSION && win_dump) refreshDumpWindow(n); return (ret); } apt-dater/src/ui.h0000664000175000017500000000705515131461765012122 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _UI_H #define _UI_H #ifdef HAVE_NCURSES_H #include #else #include #endif #include typedef enum { CATEGORY, GROUP, HOST, PKG, SESSION, } DrawType; typedef enum { VK_ATTACH=1, VK_CONNECT=2, VK_DUMP=4, VK_UPGRADE=8, VK_INSTALL=16, VK_REFRESH=32, VK_KILL=64, VK_ERRDIAG=128, #ifdef FEAT_HISTORY VK_PLAY=256, VK_LESS=512, #endif #ifdef FEAT_RUNCUST VK_RUNCUST=1024, #endif } EVisKeyMask; typedef enum { SC_KEY_LEFT = 0, SC_KEY_LEFT2, SC_KEY_RIGHT, SC_KEY_RIGHT2, SC_KEY_UP, SC_KEY_UP2, SC_KEY_DOWN, SC_KEY_DOWN2, SC_KEY_HOME, SC_KEY_END, SC_KEY_PPAGE, SC_KEY_NPAGE, SC_KEY_SPACE, SC_KEY_RETURN, SC_KEY_ENTER, SC_KEY_PLUS, SC_KEY_QUIT, SC_KEY_HELP, SC_KEY_FIND, SC_KEY_FILTER, SC_KEY_ATTACH, SC_KEY_KILLSESS, SC_KEY_CONNECT, SC_KEY_FILETRANS, SC_KEY_TOGGLEDUMPS, SC_KEY_REFRESH, SC_KEY_TAGACTION, SC_KEY_INSTALL, SC_KEY_UPGRADE, SC_KEY_MORE, SC_KEY_ERRDIAG, #ifdef FEAT_HISTORY SC_KEY_HISTORY, SC_KEY_PLAY, SC_KEY_LESS, #endif SC_KEY_NEXTSESS, SC_KEY_CYCLESESS, #ifdef FEAT_RUNCUST SC_KEY_RUNCUST, #endif SC_KEY_TAG, SC_KEY_TAGMATCH, SC_KEY_UNTAGMATCH, SC_KEY_UNUSED, SC_MAX, } EShortCuts; #define TAGGED_MASK 100000 typedef struct _drawnode { #ifndef NDEBUG etype _type; #endif void *p; DrawType type; gboolean extended; gboolean selected; guint scrpos; guint elements; guint etagged; int attrs; struct _drawnode *parent; } DrawNode; struct HostFlag { gint flag; gchar *code; gchar *descr; }; gboolean ctrlKeyEnter(GList *hosts); void doUI (GList *hosts); void refreshUI(); void refreshDraw(); gboolean ctrlUI (GList *); void cleanUI(); void injectKey(int); void applyFilter(GList *hosts); void disableInput(); void enableInput(); void reorderScrpos(guint); gchar *getStrFromDrawNode (DrawNode *); void freeDrawNode (DrawNode *); void notifyUser(); void drawStatus (char *str, gboolean drawoldest); #include "apt-dater.h" extern gchar maintainer[48]; extern gchar *drawCategories[]; extern const struct HostFlag hostFlags[]; #ifndef KEY_RETURN # define KEY_RETURN 13 #endif #ifndef KEY_ESC # define KEY_ESC 27 #endif #ifndef KEY_FWWORD # define KEY_FWWORD 102 #endif #ifndef KEY_BWWORD # define KEY_BWWORD 98 #endif #ifndef KEY_KILLEOW # define KEY_KILLEOW 100 #endif #ifndef KEY_KILLBOW # define KEY_KILLBOW 23 #endif #define ctrl(c) ((c)-'@') #define remln(cols) hline(' ', cols); #define mvremln(y, x, cols) mvhline(y, x, ' ', cols); #define mvwremln(win, y, x, cols) mvwhline(win, y, x, ' ', cols); #endif /* _UI_H */ apt-dater/src/extern.h0000664000175000017500000000217115131461765013004 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _EXTERN_H #define _EXTERN_H extern CfgFile *cfg; extern GMainLoop *loop; extern gboolean rebuilddl; extern time_t oldest_st_mtime; extern int uicolors[]; #endif /* _EXTERN_H */ apt-dater/src/glue.h0000664000175000017500000000244715131461765012441 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GLUE_H #define _GLUE_H #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #if !GLIB_CHECK_VERSION(2, 14, 0) guint g_timeout_add_seconds (guint interval, GSourceFunc function, gpointer data); #endif #if !GLIB_CHECK_VERSION(2, 34, 0) gboolean g_spawn_check_exit_status (gint exit_status, GError **error); #endif #endif /* _GLUE_H */ apt-dater/src/sighandler.c0000664000175000017500000000332515131461765013614 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "ui.h" #include "sighandler.h" #include "lock.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif static volatile int sigintcnt = 0; static gboolean ignsigint = FALSE; static void sigintSigHandler(int sig) { switch(sig) { case SIGINT: if(ignsigint == TRUE) break; if (sigintcnt++ > 1) { cleanUI(); cleanupLocks(); exit(EXIT_FAILURE); } else { cleanUI(); refreshUI(); g_main_loop_quit (loop); } break; } /* switch(sig) */ } static void sigtermSigHandler() { cleanUI(); refreshUI(); g_main_loop_quit (loop); } void setSigHandler() { signal(SIGINT, sigintSigHandler); signal(SIGTERM, sigtermSigHandler); } void ignoreSIGINT(gboolean ign) { /* Disable SIGINT for before use exec. */ ignsigint=ign; } apt-dater/src/exec.h0000664000175000017500000000243115131461765012422 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _EXEC_H #define _EXEC_H #include "ui.h" gboolean ssh_cmd_refresh(HostNode *n); gboolean ssh_cmd_upgrade(HostNode *n, const gboolean detached); gboolean ssh_cmd_install(HostNode *n, gchar *package, const gboolean detached); void ssh_connect(HostNode *n, const gboolean detached); void sftp_connect(HostNode *n); #endif /* _EXEC_H */ apt-dater/src/parsecmd.c0000664000175000017500000000472015131461765013272 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "apt-dater.h" #include "parsecmd.h" #include "ui.h" gchar *parse_string(const gchar *src, const HostNode *n) { if(!n) return g_strdup(src); gint i = 0; GString *h = g_string_sized_new(strlen(src)); while(src[i]) { if((src[i] != '%') || (src[i+1] == 0)) { g_string_append_c(h, src[i++]); continue; } i++; switch(src[i]) { case 'h': g_string_append(h, n->hostname); break; case 'H': g_string_append(h, (n->ssh_host ? n->ssh_host : n->hostname)); if(n->ssh_port) { g_string_append(h, ":"); g_string_append_printf(h, "%d", n->ssh_port); } break; case 'u': g_string_append(h, n->ssh_user); break; case 'U': if(n->ssh_user) { g_string_append(h, n->ssh_user); g_string_append(h, "@"); } break; case 'p': g_string_append_printf(h, "%d", n->ssh_port); break; case 'm': g_string_append(h, maintainer); break; default: g_string_append_c(h, src[i]); continue; } i++; } return g_string_free(h, FALSE); } gboolean parse_cmdline(const char *s, int *argcPtr, char ***argvPtr, const HostNode *n) { gint i; const gchar **argv; if(poptParseArgvString(s, argcPtr, &argv) < 0) return FALSE; *argvPtr = g_new0(char*, *argcPtr+1); if(n) for(i=0;i<*argcPtr;i++) (*argvPtr)[i] = parse_string(argv[i], n); return TRUE; } apt-dater/src/tag.h0000664000175000017500000000177015131461765012256 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ gboolean compHostWithPattern(HostNode *, gchar *, gsize); apt-dater/src/stats.c0000664000175000017500000003121315131461765012627 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "apt-dater.h" #include "exec.h" #include "ttymux.h" #include "stats.h" #include "lock.h" #include "autoref.h" #include "clusters.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif void stats_changed(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { HostNode *n = user_data; g_assert(n); switch(event_type) { case G_FILE_MONITOR_EVENT_DELETED: n->category = (g_file_test(n->statstmpf, G_FILE_TEST_EXISTS) ? C_REFRESH : C_UNKNOWN); rebuilddl = TRUE; /* Trigger a DrawList rebuild */ ;; case G_FILE_MONITOR_EVENT_CREATED: getUpdatesFromStat(n); rebuilddl = TRUE; /* Trigger a DrawList rebuild */ ;; default: return; ;; } } void stats_initialize(HostNode *n) { GFile *path = g_file_new_for_path(n->statsfile); n->mon_stats = g_file_monitor(path, G_FILE_MONITOR_NONE, NULL, NULL); g_object_unref(path); g_signal_connect(n->mon_stats, "changed", G_CALLBACK(stats_changed), n); } gchar *getStatsFile(const HostNode *n) { if(g_file_test(n->statsfile, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS) == FALSE) return NULL; return n->statsfile; } gboolean prepareStatsFile(HostNode *n) { g_unlink(n->statsfile); g_unlink(n->statstmpf); n->fpstat = fopen(n->statstmpf, "wx"); return n->fpstat != NULL; } void refreshStatsOfNode(gpointer p) { HostNode *n = (HostNode *)p; if(n->fpstat ) { fclose(n->fpstat); n->fpstat = NULL; g_rename(n->statstmpf, n->statsfile); } /* getUpdatesFromStat(n);*/ unsetLockForHost(n); // rebuilddl = TRUE; /* Trigger a DrawList rebuild */ } gboolean setStatsFileFromIOC(GIOChannel *ioc, GIOCondition condition, gpointer n) { gchar *buf = NULL; GError *error = NULL; gsize bytes; gboolean r = TRUE; GIOStatus iostatus = G_IO_STATUS_NORMAL; if(!n) return(FALSE); if (condition & (G_IO_HUP | G_IO_PRI | G_IO_IN)) { buf = (gchar *) g_malloc0 (g_io_channel_get_buffer_size (ioc) + 1); r = FALSE; while(iostatus == G_IO_STATUS_NORMAL && ((HostNode *) n)->fpstat) { iostatus = g_io_channel_read_chars (ioc, buf, g_io_channel_get_buffer_size (ioc), &bytes, NULL); if(iostatus == G_IO_STATUS_ERROR || iostatus == G_IO_STATUS_AGAIN) break; if(*buf == 0) condition = G_IO_HUP; if(fwrite(buf, sizeof(gchar), bytes, ((HostNode *) n)->fpstat) != bytes) break; r = TRUE; } if(r == TRUE) fflush(((HostNode *) n)->fpstat); g_free(buf); } if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_io_channel_unref (ioc); if(g_io_channel_shutdown (ioc, TRUE, &error) == G_IO_STATUS_ERROR) { g_warning("%s", error->message); g_clear_error (&error); } r = FALSE; } return(r); } void freePkgNode(PkgNode *n) { if(n) { g_free(n->package); g_free(n->version); if(n->data) g_free(n->data); g_free(n); } } static void freePackages(HostNode *n) { if(n && n->packages) { #ifdef FEAT_AUTOREF autoref_rem_host_info(n); #endif g_list_foreach(n->packages, (GFunc) freePkgNode, NULL); g_list_free(n->packages); n->packages = NULL; } } static gint cmpPackages(gconstpointer a, gconstpointer b) { return((((PkgNode *)a)->flag != ((PkgNode *)b)->flag) ? ((PkgNode *)a)->flag - ((PkgNode *)b)->flag : g_ascii_strcasecmp(((PkgNode *)a)->package, ((PkgNode *)b)->package)); } gboolean getUpdatesFromStat(HostNode *n) { char line[STATS_MAX_LINE_LEN]; char buf[256] = "\0"; FILE *fp; PkgNode *pkgnode = NULL; gint status=0, i; gchar **argv = NULL; gboolean adproto = FALSE; gfloat adpver = 0; gboolean adperr = FALSE; if(!n) return (FALSE); #ifdef FEAT_AUTOREF struct stat sbuf; if(!stat(n->statsfile, &sbuf)) n->last_upd = sbuf.st_mtime; #endif if(!(fp = (FILE *) g_fopen(n->statsfile, "r"))) { n->category = (g_file_test(n->statstmpf, G_FILE_TEST_EXISTS) ? C_REFRESH : C_UNKNOWN); return (TRUE); } n->status = 0; n->nupdates = 0; n->nextras = 0; n->nholds = 0; n->nbrokens = 0; n->forbid = 0; n->uuid[0] = 0; n->nrkstate = ADP_STATUS_NRK_UNKNOWN; freePackages(n); if(n->lsb_distributor) { g_free(n->lsb_distributor); n->lsb_distributor = NULL; } if(n->lsb_release) { g_free(n->lsb_release); n->lsb_release = NULL; } if(n->lsb_codename) { g_free(n->lsb_codename); n->lsb_codename = NULL; } if(n->uname_kernel) { g_free(n->uname_kernel); n->uname_kernel = NULL; } if(n->uname_machine) { g_free(n->uname_machine); n->uname_machine = NULL; } if(n->virt) { g_free(n->virt); n->virt = NULL; } if(n->adperr) { g_free(n->adperr); n->adperr = NULL; } if(n->kernelrel) { g_free(n->kernelrel); n->kernelrel = NULL; } #ifdef FEAT_CLUSTERS cluster_host_reset(n); #endif int linesok = 0; while(fgets(line, STATS_MAX_LINE_LEN, fp)) { /* Remove any whitespace from the line. */ g_strchomp(line); if (sscanf((gchar *) line, ADP_PATTERN_KERNELINFO, &status, buf) && !n->kernelrel) { n->kernelrel = g_strdup(buf); if(n->nrkstate == ADP_STATUS_NRK_UNKNOWN) { switch(status){ case 1: n->status = n->status | HOST_STATUS_KERNELVERUPGR; break; case 2: n->status = n->status | HOST_STATUS_KERNELUNKNOWN; break; } } linesok++; continue; } if(sscanf(line, ADP_PATTERN_ADPROTO, &adpver)) { adproto = TRUE; continue; } if(!strncmp("STATUS: ", line, 8)) { argv = g_strsplit(&line[8], "|", 0); if(!argv) continue; i=0; while(argv[i]) i++; /* ignore invalid lines */ if(i < 3) { g_strfreev(argv); continue; } pkgnode = g_new0(PkgNode, 1); #ifndef NDEBUG pkgnode->_type = T_PKGNODE; #endif pkgnode->package = g_strdup(argv[0]); pkgnode->version = g_strdup(argv[1]); if (strlen(argv[2]) > 3) pkgnode->data = g_strdup(&argv[2][2]); switch(argv[2][0]) { case 'u': n->status = n->status | HOST_STATUS_PKGUPDATE; pkgnode->flag = HOST_STATUS_PKGUPDATE; n->nupdates++; break; case 'h': n->status = n->status | HOST_STATUS_PKGKEPTBACK; pkgnode->flag = HOST_STATUS_PKGKEPTBACK; n->nholds++; break; case 'x': n->status = n->status | HOST_STATUS_PKGEXTRA; pkgnode->flag = HOST_STATUS_PKGEXTRA; n->nextras++; break; case 'b': n->status = n->status | HOST_STATUS_PKGBROKEN; pkgnode->flag = HOST_STATUS_PKGBROKEN; n->nbrokens++; break; case 'i': break; default: g_free(pkgnode->package); g_free(pkgnode->version); g_free(pkgnode); g_strfreev(argv); continue; } g_strfreev(argv); n->packages = g_list_insert_sorted(n->packages, pkgnode, cmpPackages); linesok++; continue; } if(!strncmp("LSBREL: ", line, 8)) { argv = g_strsplit(&line[8], "|", 0); if(!argv) continue; i=0; while(argv[i]) i++; /* ignore invalid lines */ if(i < 3) { g_strfreev(argv); continue; } if(strlen(argv[0]) > 0) { n->lsb_distributor = g_strdup(argv[0]); n->lsb_release = g_strdup(argv[1]); n->lsb_codename = g_strdup(argv[2]); } g_strfreev(argv); linesok++; continue; } if (sscanf((gchar *) line, ADP_PATTERN_VIRT, buf)) { n->virt = g_strdup(buf); if (strcmp(n->virt, "Unknown") && strcmp(n->virt, "Physical")) n->status = n->status | HOST_STATUS_VIRTUALIZED; linesok++; continue; } if (sscanf((gchar *) line, ADP_PATTERN_ADPERR, buf)) { n->adperr = g_strdup(buf); adperr = TRUE; continue; } if (sscanf((gchar *) line, ADP_PATTERN_UNAME, buf)) { gchar *s = strchr(buf, '|'); if (s) { s[0] = 0; n->uname_kernel = g_strdup(buf); n->uname_machine = g_strdup(s+1); linesok++; } continue; } if (sscanf((gchar *) line, ADP_PATTERN_FORBID, &n->forbid)) continue; if (sscanf((gchar *) line, "UUID: %" QUOTE(UUID_STRLEN) "s", n->uuid)) { n->uuid[UUID_STRLEN-1] = 0; linesok++; continue; } #ifdef FEAT_CLUSTERS char cluster[ADP_STRLEN_CLUSTER]; if (sscanf((gchar *) line, ADP_PATTERN_CLUSTER, cluster)) { cluster[ADP_STRLEN_CLUSTER-1] = 0; cluster_host_add(n, cluster); linesok++; continue; } #endif gint nrksta; if (sscanf((gchar *) line, ADP_PATTERN_NRKSTATUS, &nrksta)) { n->nrkstate = nrksta; switch(nrksta) { case ADP_STATUS_NRK_NOUPGR: n->status = (n->status | HOST_STATUS_KERNELVERUPGR | HOST_STATUS_KERNELUNKNOWN) ^ (HOST_STATUS_KERNELVERUPGR | HOST_STATUS_KERNELUNKNOWN); break; case ADP_STATUS_NRK_ABIUPGR: n->status = (n->status | HOST_STATUS_KERNELVERUPGR | HOST_STATUS_KERNELUNKNOWN) ^ (HOST_STATUS_KERNELVERUPGR | HOST_STATUS_KERNELUNKNOWN); n->status = n->status | HOST_STATUS_KERNELABIUPGR; break; case ADP_STATUS_NRK_VERUPGR: n->status = (n->status | HOST_STATUS_KERNELVERUPGR | HOST_STATUS_KERNELUNKNOWN) ^ HOST_STATUS_KERNELUNKNOWN; break; default: n->status = n->status | HOST_STATUS_KERNELUNKNOWN; break; } linesok++; continue; } } if((!adproto && linesok > 10) || (adproto && adpver < ADP_FEATVER_ADPERR && linesok > 10) || (adproto && adpver >= ADP_FEATVER_ADPERR && !adperr)) { if (n->status & HOST_STATUS_PKGBROKEN) n->category = C_BROKEN_PKGS; else if(n->status & HOST_STATUS_PKGUPDATE) n->category = C_UPDATES_PENDING; else n->category = C_UP_TO_DATE; #ifdef FEAT_AUTOREF autoref_add_host_info(n); #endif } else n->category = C_UNKNOWN; fclose(fp); return(TRUE); } gchar *getStatsContent(const HostNode *n) { gchar *c = NULL; if(!g_file_get_contents(n->statsfile, &c, NULL, NULL)) return NULL; return c; } gboolean refreshStats(GList *hosts) { GList *ho; gboolean r = TRUE; static gboolean upd_pending = FALSE; gboolean n_upd_pending = FALSE; ho = g_list_first(hosts); #ifdef FEAT_AUTOREF static gboolean do_autoref = TRUE; gint num_in_refresh = 0; #endif while(ho) { HostNode *n = (HostNode *) ho->data; if(TTYMUX_GET_SESSIONS(n)) { if(n->category != C_SESSIONS) { n->category = C_SESSIONS; rebuilddl = TRUE; } } else { if(n->category == C_UPDATES_PENDING) n_upd_pending = TRUE; if (n->category == C_SESSIONS) n->category = C_REFRESH_REQUIRED; if(n->category == C_REFRESH_REQUIRED) { /* Try to get a lock for the host. */ gint rsetlck = setLockForHost(n); /* We don't got the lock. */ if(rsetlck == -1) { n->status|= HOST_STATUS_LOCKED; freePackages(n); } /* We got the lock. */ else if (rsetlck == 0) { if(n->status & HOST_STATUS_LOCKED) { refreshStatsOfNode(n); /* n->status ^= HOST_STATUS_LOCKED; */ } else { n->category = C_REFRESH; rebuilddl = TRUE; freePackages(n); if(ssh_cmd_refresh(n) == FALSE) { n->category = C_UNKNOWN; } } /* Something weird happend. */ } else { n->category = C_UNKNOWN; rebuilddl = TRUE; } } } #ifdef FEAT_AUTOREF if(n->category == C_REFRESH) num_in_refresh++; #endif ho = g_list_next(ho); } #ifdef FEAT_TCLFILTER applyFilter(hosts); #endif if(n_upd_pending && !upd_pending) notifyUser(); upd_pending = n_upd_pending; #ifdef FEAT_AUTOREF if(cfg->auto_refresh) { if (num_in_refresh == 0) { if(do_autoref) { if (autoref_trigger_auto()) drawStatus (_("Auto refresh triggered..."), FALSE); do_autoref = FALSE; } } else do_autoref = TRUE; } #endif return(r); } void getOldestMtime(GList *hosts) { GList *ho; struct stat stat_p; HostNode *n; oldest_st_mtime = time(NULL); ho = g_list_first(hosts); while(ho) { n = (HostNode *) ho->data; if(getStatsFile(n)) { if(!stat(n->statsfile, &stat_p)) { if(difftime(stat_p.st_mtime, oldest_st_mtime) < 0) oldest_st_mtime = stat_p.st_mtime; } } ho = g_list_next(ho); } } apt-dater/src/history.c0000664000175000017500000001257015131461765013177 0ustar meme/* apt-dater - terminal-based remote package update manager * * Authors: * Thomas Liske * * Copyright Holder: * 2008-2015 (C) IBH IT-Service GmbH [https://www.ibh.de/apt-dater/] * * License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "apt-dater.h" #include "history.h" #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef FEAT_HISTORY HistoryEntry *history_read_meta(const gchar *fn, const gchar *tfn) { GKeyFile *kf = g_key_file_new(); if(!g_key_file_load_from_file(kf, fn, G_KEY_FILE_NONE, NULL)) { g_key_file_free(kf); return NULL; } HistoryEntry *he = g_new(HistoryEntry, 1); he->ts = g_key_file_get_integer(kf, "Meta", "TS", NULL); he->maintainer = g_key_file_get_string(kf, "Meta", "Maintainer", NULL); he->action = g_key_file_get_string(kf, "Meta", "Action", NULL); he->data = g_key_file_get_string(kf, "Meta", "Data", NULL); he->duration = (gint) g_key_file_get_double(kf, "Meta", "Duration", NULL); he->errpattern = g_key_file_get_string(kf, "Meta", "ErrPattern", NULL); g_key_file_free(kf); return he; } void history_write_meta(const gchar *fn, const HistoryEntry *he) { GKeyFile *kf = g_key_file_new(); g_key_file_set_integer(kf, "Meta", "TS", he->ts); if(he->maintainer) g_key_file_set_string(kf, "Meta", "Maintainer", he->maintainer); if(he->action) g_key_file_set_string(kf, "Meta", "Action", he->action); if(he->data) g_key_file_set_string(kf, "Meta", "Data", he->data); gchar *data = g_key_file_to_data(kf, NULL, NULL); g_key_file_free(kf); if(!data) return; GError *error = NULL; g_file_set_contents(fn, data, -1, &error); if (error) { g_error("%s", error->message); g_clear_error(&error); } g_free(data); } static gint cmp_he(gconstpointer a, gconstpointer b) { const HistoryEntry *ha = (HistoryEntry *)a; const HistoryEntry *hb = (HistoryEntry *)b; if(ha->ts < hb->ts) return -1; if(ha->ts == hb->ts) return 0; return 1; } GList *history_get_entries(const CfgFile *cfg, const HostNode *n) { const gchar *fn; GList *hel = NULL; gchar *path = history_path(cfg, n); GDir *dir = g_dir_open(path, 0, NULL); if(!dir) { g_free(path); return NULL; } while((fn = g_dir_read_name(dir))) { gchar *meta = g_strdup_printf("%s/%s/meta", path, fn); gchar *timing = g_strdup_printf("%s/%s/timingfile", path, fn); HistoryEntry *he = history_read_meta(meta, timing); g_free(timing); g_free(meta); if(he) { he->path = g_strdup_printf("%s/%s", path, fn); hel = g_list_insert_sorted(hel, he, cmp_he); } } g_dir_close(dir); g_free(path); return hel; } HistoryEntry *history_recent_entry(const CfgFile *cfg, const HostNode *n) { gchar *path = history_ts_path(cfg, n); gchar *meta = g_strdup_printf("%s/meta", path); gchar *timing = g_strdup_printf("%s/timingfile", path); HistoryEntry *he = history_read_meta(meta, timing); if(he) { he->path = path; } else g_free(path); g_free(timing); g_free(meta); return he; } static void free_hel(gpointer data, gpointer user_data) { HistoryEntry *he = (HistoryEntry *)data; g_free(he->path); g_free(he->maintainer); g_free(he->action); g_free(he->data); g_free(he->errpattern); g_free(data); } void history_free_he(HistoryEntry *he) { free_hel(he, NULL); } void history_free_hel(GList *hel) { g_list_foreach(hel, free_hel, NULL); g_list_free(hel); } static void history_show_cmd(gchar *cmd, gchar *param1, gchar *param2, HistoryEntry *he) { GError *error = NULL; gchar *argv[5] = { ENV_BINARY, cmd, param1, param2, NULL}; if(g_spawn_sync(he->path, argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error) == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } } void history_show_less(HistoryEntry *he) { history_show_cmd("less", "-R", "typescript", he); } void history_show_replay(HistoryEntry *he) { history_show_cmd("scriptreplay", "timingfile", NULL, he); } void history_show_less_search(HistoryEntry *he, gchar *pattern) { GError *error = NULL; gchar *argv[6] = { PKGLIBDIR"/pcre-less", pattern, "typescript", NULL}; if(g_spawn_sync(he->path, argv, NULL, G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, NULL, NULL, NULL, &error) == FALSE) { g_warning("%s", error->message); g_clear_error (&error); } } gboolean history_ts_failed(const CfgFile *cfg, HostNode *n) { gchar *p = history_ts_path(cfg, n); gchar *fn = g_strdup_printf("%s/failed", p); g_free(p); gboolean r = g_file_test(fn, G_FILE_TEST_EXISTS); g_free(fn); return r; } #endif apt-dater/README.tagging0000664000175000017500000000201615131461765013034 0ustar memeHost tagging ============ apt-dater has a 'tagging' feature like the MUA mutt (a poor man's host filter ;). Usage ===== You can tag or untag a single host by pressing the `t' key. Tag multiple hosts matching a pattern by pressing the `T' key. Hosts can be untagged by a pattern using the Ctrl + `T' key combination. The pattern is a regular expression matching the hostname. The pattern might be pretended by a tokens to apply the regex on the following host's values: ~c codename ~d distributor ~f host flags ~g group ~p package ~u update ~A all hosts After you have done your selection you can do some actions with the tagged hosts by pressing the `;' key: ; i install a package ; g refresh the hosts ; u upgrade the hosts w/ pending updates Examples ======== Tag all hosts which have installed the package apt-dater-host: ~p apt-dater-host Tag all hosts which are start with the word inet in their hostname: ^inet Tag all hosts which have set the flag `x' and `R': ~f xR apt-dater/.travis.features0000775000175000017500000000057415131461765013675 0ustar meme#!/usr/bin/perl use strict; use warnings; use Algorithm::Combinatorics qw(combinations); my @features = qw(debug xmlreport autoref history clusters tclfilter tmux); my $count = scalar @features; foreach my $c (0..$count) { foreach my $set (combinations(\@features, $c)) { print " - features=\""; print join(' ', map { "--enable-$_"; } sort @$set); print "\"\n"; } } apt-dater/README.history0000664000175000017500000000013415131461765013114 0ustar memeHistory ======= apt-dater could use `script' from the bsdutils package to record sessions. apt-dater/COPYING0000664000175000017500000004312215131461765011573 0ustar meme GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. apt-dater/m4lib/0000775000175000017500000000000015131461765011545 5ustar memeapt-dater/m4lib/adl_recursive_eval.m40000664000175000017500000000116715131461765015652 0ustar memednl adl_RECURSIVE_EVAL(VALUE, RESULT) dnl ================================= dnl Interpolate the VALUE in loop until it doesn't change, dnl and set the result to $RESULT. dnl WARNING: It's easy to get an infinite loop with some unsane input. AC_DEFUN([adl_RECURSIVE_EVAL], [_lcl_receval="$1" $2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" _lcl_receval_old='' while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do _lcl_receval_old="[$]_lcl_receval" eval _lcl_receval="\"[$]_lcl_receval\"" done echo "[$]_lcl_receval")`]) apt-dater/README.xmlreport0000664000175000017500000000146615131461765013460 0ustar memeXML Report ========== Since version 0.6.0, apt-dater can be compiled with XML report support. This feature is disabled by default, enable it with configure: $ ./configure --enable-xmlreport You need libxml2 installed to succeed, the Debian packages have XML report support enabled. Usage ===== To create a XML report, start apt-dater with command line parameter -r: $ apt-dater -r > report.xml This will refresh all hosts and reports the hosts status as XML formated output. This output can be formated to your needs using a XSLT program. You could find examples in the xmlreport directory. To process the XML file you could use xsltproc: $ xsltproc xmlreport/overview.xsl report.xml You could do anything with the results XML file. The report should be valid according the DTD found at schema/report.dtd. apt-dater/ChangeLog0000664000175000017500000004155315131461765012320 0ustar memeapt-dater (1.0.5) unstable; urgency=low * Update err pattern to ignore fail2ban string. (github pull request #151 by Herman van Rink @helmo) * Clean up whitespace and indenting. (github pull request #170 by Mike Beattie @mjbnz) * Update README.autoref link to *Attacks on Package Managers* article. (github issue request #171 by Jakob Haufe @sur5r) * Fix attribute hasupdate in report.dtd. (github pull request #172 by Juri Grabowski @gratuxri) * Fix quoting of examples in apt-dater.xml. (github pull request #174 by Juri Grabowski @gratuxri) * Cleanup and update autotools. (github pull request #180 by Stefan Bühler @stbuehler) * Fix various memory handling issues. (github pull request #182 by Stefan Bühler @stbuehler) * Fix _GNU_SOURCE redefinition warning. (@alpinelinux aports patch by Henrik Riomar @HRio) -- Thomas Liske apt-dater (1.0.4) unstable; urgency=low * tmux - bind kill-pane to `q` w/o prefix key (requires tmux 2.1+) (github pull request #130 by Stefan Bühler @stbuehler) * Travis CI: - use docker for recent Debian build environment (github issue #139 by Lukas Kallies @kallies) * Bugfixes: - Fix Tcl filters never matching due to broken return code handling. - Do not fail on make install on updating due to symlinks creation. (github issue #131 by Robin Kluth @Commifreak) - Revert a82d3f7: default config value for 'err-pattern' should not use html entities. (github issue #124 by Stephan Sürken ) - Update err-pattern to ignore fail2ban string. (github pull request #151 by Hermann van Rink @helmo) - Fix syntax of report.dtd schema. (github issue #153 by Mathieu Parent @sathieu) - Clean up whitespaces and indenting. (github pull request #170 by Mike Beattie @mjbnz) - Fix link in README.autoref. (github issue #171 by Jakob Haufe @sur5r) - Fix attribute hasupdate in report.dtd. (github pull request #172 by @gratuxri) - Fix quoting of examples in apt-dater.xml. (github pull request #174 by @gratuxri) * Update to automake 1.16. -- Thomas Liske Sun, 10 Feb 2019 22:15:45 +0100 apt-dater (1.0.3) unstable; urgency=low * adsh: ssh wrapper which records ssh sessions using script * screen: add support for SCREENDIR env variable (github pull request #111 by Thomas Wouters @twouters) * Update to automake 1.15. * Bugfixes: - man: change config filenames (s/(apt-dater|hosts).config/$1.xml/g;) (Debian Bug#793100 by Stephan Sürken ) - etc: take account of --sysconfdir (github issue #101 by Alex Dunn @dunn) - suppress I/O warnings triggered by XInclude - check libxml2 ABI interface - fix MAINTAINER env variable handling - replace MAINTAINER by AD_MAINTAINER environment variable - fix hooks/plugin-dir example and schema definition (github issue #100 by @fufroma) - fix syntax of string comparison in ssh-addonce (github pull request #103 by @jvsalo) - fix hosts stuck in refresh (Debian Bug#801994 by Stefan Bühler ) - use localstatedir in stead of static /var (github pull request #109 by Thomas Wouters @twouters) - recursive eval datarootdir fixing default value of XML_SCHEMA_DIR (github issue #112 by @kpengboy) (Debian Bug#826403 by Evgeni Golov ) - fix typo in german translation (Debian Bug#813103 by Alexander Schier ) - workaround for less: use errpattern with grep -P only and enable colorized output in less - change default screen socket dir on OS X (github issue #110 by Thomas Wouters @twouters) - tmux: silence obsolete tmux 2.2 option "status-utf8" (Debian Bug#827107 by Evgeni Golov ) - use LC_ALL=C on date call for reproducible builds (Debian Bug#797211 by Chris Lamb ) -- Thomas Liske Mon, 13 Jun 2016 15:44:30 +0200 apt-dater (1.0.2) unstable; urgency=low * Minor bugfixes by Simon Kainz : - Segfaulting due misused g_error() call (Debian Bug#767584). - Typos in manpages (Debian Bug#770444). * Drop linking binary blobs for default configs, use portable xxd based implementation (Debian Bug#767594). * Switching config engine again: libxml2 * Convertion of legacy hosts.conf to hosts.xml. * Update host states using GFileMonitor instead of polling. * Make apt-dater usable by different concurrent users sharing the same hosts and sessions. * Add support using tmux for tty muxing instead of screen (required to support different users sharing sessions). * Allow reproducible builds. (Debian Bug#789648 by Dhole ) * Do not run ssh-add for keys already in ssh-agent. -- Thomas Liske Tue, 07 Jul 2015 11:25:15 +0200 apt-dater (1.0.1) unstable; urgency=low * Bugfixes: - Documentation bugfix in hosts.config example (host identifiers must not contain dots). - lib/cmd script does not respect AD_SSH_HOST making SSHHost config option useless. - lib/cmd does not run apt-dater-host via SSH in generic-ssh handler. * Update Portuguese translation. (Closes Debian Bug#767524 by Américo Monteiro ) -- Thomas Liske Fri, 31 Oct 2014 20:36:23 +0100 apt-dater (1.0.0) unstable; urgency=low * Update German localization. * Assume target_os is linux compatible as default (fixes building on kfreebsd and hurd flavours). * Use SSH / OptionalCmdFlags on host refresh. (Closes Debian Bug#678111 by "jonas") * Fix sftp's port parameter. (Contributed by Michael Abmayer) * Don't translated shortcuts on help screen. (Contributed by Michael Abmayer) * Localize 'Oldest:' date/time display. (Closes Debian Bug#718500 by Timo Weingärtner ) * Add negative lookbehind to error detection. This stops matching lines like "No error reported.". (Closes github issue #25 by Stefan Eriksson.) * Add Portuguese translation. (Closes Debian Bug#757290 by Américo Monteiro ) * Update to automake 1.14. * Add new configuration backend using libconfig. (Closes github issue #5 and #9 by Stefan Eriksson and Debian Bug#678271 by jonas ) * ADP 7.0: Eval needrestart's kernel status to detect kernel updates. (Closes github issue #37 by GiNeR) -- Thomas Liske Tue, 28 Oct 2014 18:30:10 +0100 apt-dater (0.9.0) unstable; urgency=low * Add 'failure diagnostic' screen. (Closes Debian Bug#646902 by Markus Raab) * Document ErrPattern statement in apt-dater.conf. (Closes SF Bug by Herman van Rink) * Remove depreciated Tcl_Interp access (required for Tcl8.6+). * Include config.h before using HAVE_LOCALE_H (provided by Patrick Matthäi). * New feature: cluster support (README.clusters) * Sync to apt-dater-host 0.9.0-adp0.6: - cluster support - depend on ImVirt.pm on Debian -- Thomas Liske Tue, 29 May 2012 14:22:41 +0200 apt-dater (0.8.6) stable; urgency=low * zypper: - Fixed: zypper did not report any updates (reported by Ivan De Masi). - Use 'zypper refresh' for refreshing package repositories. * Add RELEASE header to version string on RPM queries. * Update apt-dater.conf man page. * Fix two hardening compile warnings (provided by Patrick Matthäi). * Include locale.h if available. (Closes Debian Bug#642696 by Mònica Ramírez Arceda) * Use apt-get as default package manager. (Closes Debian Bug#635048 by Felix Bartels) * Add dummy man page apt-dater-host.1 (contributed by Patrick Matthäi). -- Thomas Liske Tue, 27 Sep 2011 15:37:03 +0200 apt-dater (0.8.5) unstable; urgency=low * Fixed a key mismatch in the history view. * Fixed a selector positioning bug. * Don't use sudo on apt-dater-host status w/ aptitude. (Fixes Debian Bug#596723 by "chrysn") * Configure should check if popt.h is available. (Closes SF Bug#3045502 by Henri Salo) * Drop "[screen] enabled" config option, it doesn't work any more. (Reported by Daniel Baumann due Debian Bug#597941) * Fix unknown yum based hosts (reported on Fedora) due bad package status interpretation. (Reported by Patrick Matthäi) * Some pedantic stuff (use long options for aptitude and apt-get to ease code reading). Don't fail on untrusted/unauthenticated packages when checking for upgrades (still check for the upgrade itself). (Contributed by Mathieu PARENT due SF Bug#3158198) * Check for updates (refresh) using dist-upgrade. This show ABI-incompatible upgrades (like the recent bind9 one: http://www.debian.org/security/2010/dsa-2130.en.html). (Contributed by Mathieu PARENT due SF Bug#3158198) * Make upgrade method customizable. (Inspired by Mathieu PARENT due SF Bug#3158198) * Fixed a key mismatch in the history view. * Fixed a selector positioning bug. * Allow apt-dater-host to be used with ssh keys. (Inspired by 'Chromosom' due SF Bug#2862139) -- Andre Ellguth Tue, 01 Feb 2011 09:04:25 +0100 apt-dater (0.8.4) unstable; urgency=low * Add UUID host field to report output. * Don't fail if apt-dater-host could not get the installed kernel packages; fallback to unknown reboot status. (Closes SF Bug#2991717 by Karoly Molnar) * Use $DPKGTOOL to detect packages with updates. (Contributed by Alexandre Anriot) * Update german localization. -- Andre Ellguth Mon, 05 Jul 2010 12:51:58 +0200 apt-dater (0.8.3) unstable; urgency=low * Parameters on 'apt-dater-host install' must not be interpreted by the shell, thanks to Henri Salo. * Remove obsolete DefaultUser/DefaultPort lines from conf/apt-dater.conf.example. (Closes SF Bug#2946417 by Mathieu PARENT) * Handle fwrite return values in keyfiles.c. * Add apt-dater-host zypper version (using zypper in rug compatibility mode). (Contributed by Chris Liles) * Detect screen's socket path during configure. (Closes SF Bug#2950721 by Chris Liles) * Add .spec files for rpm packaging. * Add '-o Aptitude::Delete-Unused=false' to aptitude safe-upgrade calls. (Contributed by Patrick Matthäi) -- Andre Ellguth Mon, 29 Mar 2010 08:35:03 +0200 apt-dater (0.8.2) unstable; urgency=low * Fix apt-dater not working on PPC (Closes Debian Bug#563159 by Gefried Fuchs; thanks to Simon Richter). * Fix hotkey l for expanding not working (Closes Debian Bug#564450 by Gefried Fuchs) * Fix pressing C for file transfer gives error message (Closes Debian Bug#564459 by Gefried Fuchs) The SFTPCmd config parameter should be set to /usr/bin/sftp - the old default value does not work any more. * Fix translated confirmation questions. (Closes Debian Bug#565931 by Patrick Matthäi). * Fix Debian hosts got 'Unknown' if the had upgrades which depends on broken packages. * apt-dater-host: add --assume-yes on safe-upgrade as suggested by Stephan Sürken. This is now default but could disabled in the apt-dater-host.conf file. (Closes Debian Bug#565930) * Fix German localisation typos. (Closes SF Bug#2941509 by Micha vor dem Berge) * Fix Debian hosts become 'Unknown' if they hadn't a stock Debian kernel installed. (Closes SF Bug#2933741 by alma@arwin.hu) -- Andre Ellguth Wed, 03 Feb 2010 15:27:39 +0100 apt-dater (0.8.1) unstable; urgency=low * Add hooks on connect/install/refresh/upgrade. * Run external commands via shell script helper. * Add host history data to XML reports. * Add -n command line argument (report w/o refresh). * Extended tagging by host flags. * Fixes: - upgrade & install on filtered categories - pass --as-needed to ld - minor ui glitches (no more flicker) * Add Italian translation - contributed by Milo Casagrande . * Drop DefaultUser and DefaultPort configuration. If a host has no username or port given, let ssh select the right one. (Closes Debian Bug#559366 by "chrysn") * Update adproto to version 0.5. -- Andre Ellguth Fri, 18 Dec 2009 13:39:52 +0100 apt-dater (0.8.0) unstable; urgency=low * Add gettext support. * Hosts can now be tagged (like in mutt). * Support mutt like navigation. * Add uname fields to apt-dater-host proto. * Apply 01-aptitude_safe-upgrade.dpatch from Debian. * Improve auto refresh feature. * History feature (record ssh session with script). * Remove "Status file missing" category ("Unkown" is used instead). * apt-dater-host (debian/yum): optional cleanup after install/upgrade * apt-dater-host protocol 0.3: - broken packages support * Debug feature (adds some assertions to the code). * Fixes: - KERNELINFO status flag on Debian Lenny - documentation - set FD_CLOEXEC on lockfiles - don't use hardcoded screen dump filenames - reduce CPU cycles use by refreshing the screen more than needed - don't pass MAINTAINER on commandline, use SSH's SendEnv/AcceptEnv instead (Debian Bug#529200 by Alexander Barton) - set LC_ALL=C on apt-dater-host rather than clear LANG env variable (SF bug #2786495 by Sebastian Heinlein and #2793663 by "kepi") -- Thomas Liske Tue, 26 May 2009 16:31:41 +0200 apt-dater (0.7.0) unstable; urgency=low * New auto refresh feature * Improved search function * Improved more info page * Basic WUA client * Removed memory leak -- Andre Ellguth Wed, 21 Jan 2009 15:17:18 +0100 apt-dater (0.6.4) unstable; urgency=low * Fixed sf bugs #2308347, #2308369, #2308370 * Add host details window. -- Andre Ellguth Tue, 25 Nov 2008 15:11:55 +0100 apt-dater (0.6.3) unstable; urgency=low * The getnLine() function can now handle a input of 4096 characters. * Added support for ssh identity file per group. * Add new VIRT line to apt-dater-host protocol. -- Andre Ellguth Mon, 17 Nov 2008 15:59:30 +0100 apt-dater (0.6.2) unstable; urgency=low * Fixed 2 bugs in the dump screen function. * Add RPM only apt-dater-host script. -- Andre Ellguth Fri, 08 Aug 2008 15:19:47 +0200 apt-dater (0.6.1) unstable; urgency=low * Fixed some bugs in the search function * Changes in the XML report: - Renamed root element - Supply DTD file - Added another XSLT sample -- Andre Ellguth Tue, 05 Aug 2008 11:14:42 +0200 apt-dater (0.6.0) unstable; urgency=low * New feature: TCL host filter function (README.tclfilter) * New feature: XML report function (apt-dater -r) * A better character input function -- Andre Ellguth Tue, 29 Jul 2008 16:38:10 +0200 apt-dater (0.5.8) unstable; urgency=medium * Terminal resize handler works now correctly. * Added the scrolling function to the help page. * Global definition of control and function keys. * Preparations for TCL host filters. -- Andre Ellguth Wed, 23 Jul 2008 16:16:58 +0200 apt-dater (0.5.7-ibh1) unstable; urgency=medium * Fixed a bug in apt-dater-host which let it die on Debian Etch. -- Thomas Liske Mon, 21 Jul 2008 19:31:06 +0200 apt-dater (0.5.7) unstable; urgency=low * Fixed some bugs in the status file detection. -- Andre Ellguth Mon, 21 Jul 2008 16:49:40 +0200 apt-dater (0.5.6) unstable; urgency=low * Fixed a bug in the lock function. -- Andre Ellguth Thu, 17 Jul 2008 15:24:05 +0200 apt-dater (0.5.5.1) unstable; urgency=low * Added lsb-release as debian package dependency * Check if the LSBREL status information is empty -- Andre Ellguth Wed, 16 Jul 2008 16:58:32 +0200 apt-dater (0.5.5) unstable; urgency=low * Added support for rug, yum based distributions. * New additional informations (lsb_release, kernel release). -- Andre Ellguth Wed, 16 Jul 2008 16:58:32 +0200 apt-dater (0.5.4) unstable; urgency=low * New upstream version -- Andre Ellguth Mon, 14 Jul 2008 15:40:42 +0200 apt-dater (0.5.3-ibh1) unstable; urgency=low * Fix typo in apt-dater-host preventing 'apt-dater-host install' to work. -- Thomas Liske Thu, 10 Jul 2008 10:48:27 +0200 apt-dater (0.5.3) unstable; urgency=low * added generic apt/aptitude interface in apt-dater-host (still needed some work to support non-dpkg-based systems) -- Thomas Liske Wed, 09 Jul 2008 09:37:42 +0200 apt-dater (0.5.2) unstable; urgency=low * Added apt-dater.conf manpage. * Fixed some locking issues. -- Thomas Liske Tue, 08 Jul 2008 09:38:32 +0200 apt-dater (0.5.1) unstable; urgency=low * Documentation fixes. -- Thomas Liske Fri, 04 Jul 2008 14:50:34 +0200 apt-dater (0.5.0) unstable; urgency=low * Initial Release. -- Thomas Liske Thu, 3 Jul 2008 13:56:44 +0200