Compare commits

..
1 Commits
Author SHA1 Message Date
Alexander Shaduri af59a431b1 Tagging 0.8.4:
Linux Software RAID devices are blacklisted now. (backported from Debian).
    Attributes tab is before the capabilities tab now.
    A man page has been generously contributed by Giuseppe Iuculano
        <giuseppe@iuculano.it>.
    Smartctl version now includes the CVS snapshot date (if available).
    Windows: Look for "smartctl-nc.exe" instead of "smartctl.exe" by default.
    Windows: Use smartmontools-supplied smartctl-nc.exe by default (if found).
    Other minor changes (mainly Debian backports).
2010-12-26 19:47:20 +00:00
284 changed files with 16985 additions and 27564 deletions
-3
View File
@@ -1,3 +0,0 @@
# Make sure github recognizes header files as C++, not C.
*.h linguist-language=C++
-171
View File
@@ -1,171 +0,0 @@
name: Build - 1.1 Branch (Tier 1 platforms)
on:
# Triggers the workflow on push or pull request events but only for the branch
push:
branches: [ release/1.1 ]
pull_request:
branches: [ release/1.1 ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
SMARTMONTOOLS_INSTALLER: smartmontools-7.2-1.win32-setup.exe
SMARTMONTOOLS_URL: https://nav.dl.sourceforge.net/project/smartmontools/smartmontools/7.2/smartmontools-7.2-1.win32-setup.exe
jobs:
linux-ubuntu:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: release/1.1
- name: Install Dependencies
run: sudo apt-get install libpcre3-dev libgtkmm-3.0-dev gettext autoconf automake
- name: Run Autogen
shell: bash
working-directory: ${{runner.workspace}}/gsmartcontrol
run: ./autogen.sh
- name: Create Build Directory
shell: bash
run: mkdir -p ${{runner.workspace}}/build
- name: Configure
shell: bash
working-directory: ${{runner.workspace}}/build
run: >
$GITHUB_WORKSPACE/configure --prefix=/usr --enable-tests
- name: Build
shell: bash
working-directory: ${{runner.workspace}}/build
run: make
- name: Test
working-directory: ${{runner.workspace}}/build
shell: bash
run: make check
- name: Pack Source
shell: bash
working-directory: ${{runner.workspace}}/build
run: make dist
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: Source Packages (1.1 Branch)
path: ${{runner.workspace}}/build/gsmartcontrol-*.tar.bz2
windows-msys2:
# Disable for now, as the configure stage errors out with "The directory name is invalid."
if: ${{ false }}
runs-on: windows-latest
strategy:
matrix:
include: [
{ msystem: MINGW64, arch: x86_64, root: /mingw64 },
{ msystem: MINGW32, arch: i686, root: /mingw32 }
]
steps:
- uses: actions/checkout@v2
with:
ref: release/1.1
- uses: msys2/setup-msys2@v2
# p7zip is needed to extract smartmontools
# fontconfig owns /etc/fonts
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
mingw-w64-${{ matrix.arch }}-gtkmm3
mingw-w64-${{ matrix.arch }}-pkg-config
autoconf
automake
mingw-w64-${{ matrix.arch }}-gcc
mingw-w64-${{ matrix.arch }}-fontconfig
make
p7zip
- name: Run Autogen
shell: msys2 {0}
working-directory: ${{runner.workspace}}/gsmartcontrol
run: ./autogen.sh
- name: Create Build Directory
shell: cmd
working-directory: ${{runner.workspace}}
run: md build
- name: Configure
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build
run: ../gsmartcontrol/configure --enable-tests
- name: Build
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build
run: make
- name: Test
working-directory: ${{runner.workspace}}/build
shell: msys2 {0}
run: make check
- name: Download Package Requirements
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build
run: |
mkdir smartmontools
cd smartmontools
wget $SMARTMONTOOLS_URL
- name: Extract Package Requirements (64-bit)
if: ${{ matrix.msystem == 'MINGW64' }}
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build/smartmontools
run: >
7z -bb1 e $SMARTMONTOOLS_INSTALLER
bin64/smartctl.exe
bin64/smartctl-nc.exe
bin/update-smart-drivedb.exe
bin/drivedb.h
- name: Extract Package Requirements (32-bit)
if: ${{ matrix.msystem == 'MINGW32' }}
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build/smartmontools
run: >
7z -bb1 e $SMARTMONTOOLS_INSTALLER
bin/smartctl.exe
bin/smartctl-nc.exe
bin/update-smart-drivedb.exe
bin/drivedb.h
- name: Package ZIP
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build
run: make win-zip-dist
- name: Package NSIS
shell: msys2 {0}
working-directory: ${{runner.workspace}}/build
run: make win-dist
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: Windows Packages (1.1 Branch)
path: ${{runner.workspace}}/build/gsmartcontrol-*-win*.*
-88
View File
@@ -1,88 +0,0 @@
name: Build - 1.1 Branch (Tier 2 platforms)
on:
# Triggers the workflow on push or pull request events but only for the branch
push:
branches: [ release/1.1 ]
pull_request:
branches: [ release/1.1 ]
# Allows you to run this workflow manually from the Actions tab.
# This does not work for non-default branches, that's why we use the push / pull_request triggers above.
workflow_dispatch:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: RelWithDebInfo
jobs:
macos-homebrew:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
ref: release/1.1
- name: Install Dependencies
run: brew install pkg-config gtkmm3 pcre automake autoconf
- name: Run Autogen
shell: bash
working-directory: ${{runner.workspace}}/gsmartcontrol
run: ./autogen.sh
- name: Create Build Directory
shell: bash
run: mkdir -p ${{runner.workspace}}/build
- name: Configure
shell: bash
working-directory: ${{runner.workspace}}/build
run: >
$GITHUB_WORKSPACE/configure --prefix=/usr --enable-tests
- name: Build
shell: bash
working-directory: ${{runner.workspace}}/build
run: make
- name: Test
working-directory: ${{runner.workspace}}/build
shell: bash
run: make check
freebsd:
# Disable for now, make cannot find file2csource.sh for some reason.
if: ${{ false }} # disable for now
runs-on: macos-10.15
# env:
# variables to pass to VM
# MYTOKEN : ${{ secrets.MYTOKEN }}
steps:
- uses: actions/checkout@v2
with:
ref: release/1.1
- name: Build and test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0.1.5
with:
usesh: true
mem: 2048
prepare: >
pkg install -y pkgconf gtkmm30 gettext pcre libiconv autoconf automake
run: >
./autogen.sh
mkdir build && cd build
../configure --prefix=/usr --enable-tests
make
make check
-80
View File
@@ -1,80 +0,0 @@
# Builds
/0*
/doxygen_doc*
/win32*
/win64*
/cmake-build-*
# Build system artifacts
/autom4te.cache
/aclocal.m4
/ar-lib
/compilation_flags
/compile
/config.guess
/config.h
/config.h.in
/config.log
/config.status
/config.sub
/configure
/depcomp
/gsmartcontrol.spec
/install-sh
Makefile
Makefile.in
/missing
/stamp-h1
/version.txt
*.glade.cpp
*.ui.cpp
*.txt.cpp
/data/gsmartcontrol.appdata.xml
/data/gsmartcontrol.desktop
/data/gsmartcontrol-root
/data/nsis/distribution.txt
/data/nsis/gsmartcontrol.nsi
/debian.dist/changelog
/src/gsc_winres.rc
/src/gsmartcontrol.exe.manifest
.deps
# Non-project files
/TODO
# Doxygen generated
/doxygen_doc
# OS-generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
# IDE
/.vs
/*.pcs
/.idea
/CMakeLists.txt.user
/.kdev4
# Translations
/po/boldquot.sed
/po/en@boldquot.header
/po/en@quot.header
/po/insert-header.sin
/po/Makevars.template
/po/quot.sed
/po/remove-potcdate.sin
/po/Rules-quot
/po/gsmartcontrol.pot
/po/*.gmo
# Backup files
*~
+16
View File
@@ -0,0 +1,16 @@
0*
*.pcs
autom4te.cache
Makefile.in
configure
depcomp
config.guess
config.sub
aclocal.m4
config.h.in
missing
install-sh
*.glade.cpp
*.ui.cpp
*.txt.cpp
gsmartcontrol.spec
-1743
View File
File diff suppressed because it is too large Load Diff
-25
View File
@@ -1,25 +0,0 @@
Copyright (c) 2006-2008 Alexander Chemeris
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+6 -3
View File
@@ -18,9 +18,12 @@ If not, see <http://www.gnu.org/licenses/>.
See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details.
All files which don't have any copyright notices, as well as tests and
examples are covered under the Unlicense. See LICENSE_unlicense.txt for
details.
All files which don't have any copyright notices, tests and examples are
covered under the Whatever License. See LICENSE_whatever.txt for details.
Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt
for details.
This product includes the HZ library (or portions of it). See LICENSE_hz.txt
+2 -6
View File
@@ -9,18 +9,14 @@ All original code (except for the tests) is licensed under the Zlib License.
See LICENSE_zlib.txt for details.
All tests and examples are covered under the Unlicense. See
LICENSE_unlicense.txt for details.
All tests and examples are covered under the Whatever License. See
LICENSE_whatever.txt for details.
Portions of this software are copyright Regents of the University of
California, see LICENSE_bsd-ucb.txt for details.
Portions of this software are copyright Alexander Chemeris, see
LICENSE_bsd-ac.txt for details.
Portions of this software are licensed under Boost Software License, see
LICENSE_boost_1_0.txt and individual source files for copyright information.
+28
View File
@@ -0,0 +1,28 @@
Copyright (c) 2005, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-25
View File
@@ -1,25 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+12
View File
@@ -0,0 +1,12 @@
Whatever License
THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
WARRANTY. IN NO EVENT WILL THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES
ARISING FROM THE USE OF THIS SOFTWARE.
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
+69 -232
View File
@@ -6,7 +6,7 @@ SUBDIRS = data debian.dist src
# These files are actually needed for compilation.
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt
# For the files to be bundled with the distribution, specify them in
@@ -16,13 +16,13 @@ noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt
# Some of these files are actually needed for compilation (see src/res/Makefile.am).
dist_doc_DATA = AUTHORS.txt ChangeLog NEWS README.txt \
LICENSE_boost_1_0.txt \
LICENSE_bsd-ac.txt \
LICENSE_bsd-ucb.txt \
LICENSE_gpl2.txt \
LICENSE_gpl3.txt \
LICENSE_gsmartcontrol.txt \
LICENSE_hz.txt \
LICENSE_unlicense.txt \
LICENSE_pcrecpp.txt \
LICENSE_whatever.txt \
LICENSE_zlib.txt
@@ -35,272 +35,109 @@ nobase_dist_doc_DATA = contrib/cron-based_noadmin/README \
# Extra files bundled with distribution.
# file2csource.sh is needed for building but not installed.
EXTRA_DIST = COPYING INSTALL configure autogen.sh \
gsmartcontrol.kdev4 \
EXTRA_DIST = COPYING TODO INSTALL configure autogen.sh \
baqup gsmartcontrol.kdevelop \
templates \
file2csource.sh \
gsmartcontrol.spec
# PCH support
global_macros.h.gch: $(top_srcdir)/src/global_macros.h
$(CXX) $(CXXFLAGS) $(DEFS) -x "c++-header" $(top_srcdir)/src/global_macros.h -o global_macros.h.gch
gch: global_macros.h.gch
gch-clean:
rm -f global_macros.h.gch
# rpm support
src-rpm: dist
rpmbuild -ts $(distdir).tar.bz2
# Don't use this - it does something weird, and gcc errors out with "long long long" errors, etc...
#rpm: dist
# rpmbuild -ta $(distdir).tar.bz2
# NSIS installer support.
# Requires installed NSIS, smartctl-nc.exe, smartctl.exe, update-smart-drivedb.exe.
# Gtkmm and pcre dlls are required if linking against them.
# Requires wine, installed NSIS, gtkmm dlls, pcre dlls, smartctl-nc.exe, smartctl.exe.
# dos2unix on build machine.
# Execute only with win32 build present.
WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-@WINDOWS_SUFFIX@
WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-win32
win-dist: win-dist-prepare nsis-dist-nocleanup win-zip-dist
win-dist-clean:
rm -rf gsmartcontrol-*.exe gsmartcontrol-*.zip nsis-dist $(WIN_ZIP_NAME)
win-dist: win-dist-nocleanup win-dist-cleanup
win-dist-prepare: all win-dist-clean
$(MKDIR_P) nsis-dist
win-dist-nocleanup: win-dist-prepare nsis-dist-nocleanup win-zip-dist-nocleanup
cp data/nsis/gsmartcontrol.nsi $(top_srcdir)/data/nsis/nsi_* nsis-dist/
cp $(top_srcdir)/data/gsmartcontrol.ico $(top_srcdir)/data/icon_*.png nsis-dist/
win-dist-cleanup: win-zip-dist-cleanup nsis-dist-cleanup
rm -rf win-dist
$(MKDIR_P) nsis-dist/doc
cp data/nsis/distribution.txt nsis-dist/doc/
cp $(top_srcdir)/AUTHORS.txt $(top_srcdir)/LICENSE_* $(top_srcdir)/README.txt nsis-dist/doc/
cp $(top_srcdir)/NEWS nsis-dist/doc/NEWS.txt
win-dist-clean: win-dist-cleanup
rm -f gsmartcontrol-*.exe gsmartcontrol-*.zip
unix2dos nsis-dist/*.nsi nsis-dist/doc/*.txt
cp "@WINDOWS_DLLS_LOCATION@"/libatkmm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libcairomm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libgdkmm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libgiomm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libglibmm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libgtkmm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libpangomm-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/libsigc-*.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/pcre3.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/mingwm10.dll nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/smartctl-nc.exe nsis-dist/
cp "@WINDOWS_DLLS_LOCATION@"/smartctl.exe nsis-dist/
cp src/gsmartcontrol.exe nsis-dist/
$(STRIP) nsis-dist/*.exe
mv nsis-dist/mingwm10.dll nsis-dist/mingwm10.dll_
$(STRIP) --strip-debug --strip-unneeded nsis-dist/*.dll
mv nsis-dist/mingwm10.dll_ nsis-dist/mingwm10.dll
win-dist-prepare-all: nsis-dist-prepare win-zip-dist-prepare
nsis-dist: nsis-dist-nocleanup nsis-cleanup
# All of GTK+.
# This includes both MSYS and OpenSUSE/mingw patterns.
GTK_BIN_FILES = gdk-pixbuf-query-loaders.exe \
gspawn-win32-helper-console.exe \
gspawn-win32-helper.exe \
gspawn-win64-helper-console.exe \
gspawn-win64-helper.exe \
gtk-query-immodules-3.0.exe \
gtk-update-icon-cache-3.0.exe \
libatk-1.*.dll \
libatkmm-1.*.dll \
libbrotlidec.dll \
libbrotlicommon.dll \
libbz2-*.dll \
libcairo-*.dll \
libcairomm-1*.dll \
libdatrie-*.dll \
libepoxy-*.dll \
libffi-*.dll \
libexpat-*.dll \
libfontconfig-*.dll \
libfreetype-*.dll \
libfribidi-*.dll \
libgdk-3*.dll \
libgdk_pixbuf-2.*.dll \
libgdkmm-3.*.dll \
libgio-2.*.dll \
libgiomm-2.*.dll \
libglib-2.*.dll \
libglibmm*2.*.dll \
libgmodule-2.*.dll \
libgobject-2.*.dll \
libgraphite*.dll \
libgthread-2.*.dll \
libgtk-3*.dll \
libgtkmm-3*.dll \
libharfbuzz*.dll \
libiconv-*.dll \
libintl*.dll \
libjasper*.dll \
libjpeg*.dll \
liblzma*.dll \
libpango*-1.*.dll \
libpixman*.dll \
libpng16-*.dll \
libsigc-2.*.dll \
libssp-*.dll \
libstdc++-*.dll \
libthai-*.dll \
libtiff-*.dll \
libwinpthread-*.dll \
libxml2-*.dll \
zlib1.dll \
libz.dll
# Don't strip smartctl (it will only change the header), and
# don't strip update-smart-drivedb (it will break its CRC check).
win-dist-prepare: all
$(MKDIR_P) win-dist
cp $(top_srcdir)/data/icon_*.png win-dist/
$(MKDIR_P) win-dist/doc
cp data/nsis/distribution.txt win-dist/doc/
cp $(top_srcdir)/AUTHORS.txt $(top_srcdir)/LICENSE_* $(top_srcdir)/README.txt win-dist/doc/
cp $(top_srcdir)/NEWS win-dist/doc/NEWS.txt
# The manifest is already embedded in exe
# cp src/gsmartcontrol.exe src/gsmartcontrol.exe.manifest win-dist/
cp src/gsmartcontrol.exe win-dist/
unix2dos win-dist/doc/*.txt
cp -p "@WINDOWS_SYSROOT@"/bin/drivedb.h win-dist/
cp -p "@WINDOWS_SYSROOT@"/bin/smartctl-nc.exe win-dist/
cp -p "@WINDOWS_SYSROOT@"/bin/smartctl.exe win-dist/
cp -p "@WINDOWS_SYSROOT@"/bin/update-smart-drivedb.exe win-dist/
for file in $(GTK_BIN_FILES); do for f in "@WINDOWS_SYSROOT@/bin/"$$file; do if test -f "$${f}"; then cp -p "$${f}" win-dist/; fi; done; done
# <prefix>/etc/gtk-3.0/ should contain settings.ini with a win32 theme.
$(MKDIR_P) win-dist/etc
$(MKDIR_P) win-dist/etc/fonts
$(MKDIR_P) win-dist/etc/gtk-3.0
cp -p "@WINDOWS_SYSROOT@"/etc/fonts/fonts.conf win-dist/etc/fonts/
# cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/im-multipress.conf win-dist/etc/gtk-3.0/
# cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/settings.ini win-dist/etc/gtk-3.0/
# Without loaders, images and native file dialog do not work.
$(MKDIR_P) win-dist/lib
$(MKDIR_P) win-dist/lib/gdk-pixbuf-2.0
cp -p -r "@WINDOWS_SYSROOT@"/lib/gdk-pixbuf-2.0/2.10.0 win-dist/lib/gdk-pixbuf-2.0
$(MKDIR_P) win-dist/share
cp -p -r "@WINDOWS_SYSROOT@"/share/themes win-dist/share
# needed for file chooser
$(MKDIR_P) win-dist/share/glib-2.0
cp -p -r "@WINDOWS_SYSROOT@"/share/glib-2.0/schemas win-dist/share/glib-2.0
# $(MKDIR_P) win-dist/share/icons
# cp -p "@WINDOWS_SYSROOT@"/share/icons/hicolor win-dist/share/icons
# Needed for window titlebar (if using client-side decorations),
# tree sorting indicators, GUI icons.
# Note that the current Adwaita uses "ui" directory, while win32 theme expects "actions".
$(MKDIR_P) win-dist/share/icons/Adwaita
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/index.theme win-dist/share/icons/Adwaita
$(MKDIR_P) win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions
$(MKDIR_P) win-dist/share/icons/Adwaita/16x16/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/status
$(MKDIR_P) win-dist/share/icons/Adwaita/16x16/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/devices/drive-harddisk.png win-dist/share/icons/Adwaita/16x16/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/devices/media-optical.png win-dist/share/icons/Adwaita/16x16/devices
# $(MKDIR_P) win-dist/share/icons/Adwaita/22x22/status
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/22x22/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/22x22/status
$(MKDIR_P) win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions
$(MKDIR_P) win-dist/share/icons/Adwaita/24x24/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/devices/drive-harddisk.png win-dist/share/icons/Adwaita/24x24/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/devices/media-optical.png win-dist/share/icons/Adwaita/24x24/devices
$(MKDIR_P) win-dist/share/icons/Adwaita/24x24/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/status
$(MKDIR_P) win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions
$(MKDIR_P) win-dist/share/icons/Adwaita/32x32/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/devices/drive-harddisk.png win-dist/share/icons/Adwaita/32x32/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/devices/media-optical.png win-dist/share/icons/Adwaita/32x32/devices
$(MKDIR_P) win-dist/share/icons/Adwaita/32x32/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/status
$(MKDIR_P) win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions
$(MKDIR_P) win-dist/share/icons/Adwaita/48x48/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/devices/drive-harddisk.png win-dist/share/icons/Adwaita/48x48/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/devices/media-optical.png win-dist/share/icons/Adwaita/48x48/devices
$(MKDIR_P) win-dist/share/icons/Adwaita/48x48/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/status
$(MKDIR_P) win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions
# $(MKDIR_P) win-dist/share/icons/Adwaita/64x64/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/devices/drive-harddisk.png win-dist/share/icons/Adwaita/64x64/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/devices/media-optical.png win-dist/share/icons/Adwaita/64x64/devices
$(MKDIR_P) win-dist/share/icons/Adwaita/64x64/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/status
$(MKDIR_P) win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions
# $(MKDIR_P) win-dist/share/icons/Adwaita/96x96/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/devices/drive-harddisk.png win-dist/share/icons/Adwaita/96x96/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/devices/media-optical.png win-dist/share/icons/Adwaita/96x96/devices
$(MKDIR_P) win-dist/share/icons/Adwaita/96x96/status
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/status
# $(MKDIR_P) win-dist/share/icons/Adwaita/256x256/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/devices/drive-harddisk.png win-dist/share/icons/Adwaita/256x256/devices
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/devices/media-optical.png win-dist/share/icons/Adwaita/256x256/devices
# $(MKDIR_P) win-dist/share/icons/Adwaita/256x256/status
# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/256x256/status
$(MKDIR_P) win-dist/share/icons/Adwaita/512x512/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/512x512/devices/drive-harddisk.png win-dist/share/icons/Adwaita/512x512/devices
cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/512x512/devices/media-optical.png win-dist/share/icons/Adwaita/512x512/devices
# other
cp -p "@WINDOWS_SYSROOT@"/bin/libgcc_s_*.dll win-dist/
cp -p "@WINDOWS_SYSROOT@"/bin/libpcre-1.dll win-dist/
cp -p "@WINDOWS_SYSROOT@"/bin/libpcrecpp-0.dll win-dist/
nsis-dist-prepare: win-dist-prepare
cp -a win-dist nsis-dist
cp $(top_srcdir)/data/gsmartcontrol.ico nsis-dist/
cp data/nsis/*.nsi $(top_srcdir)/data/nsis/nsi_* nsis-dist/
unix2dos nsis-dist/*.nsi
nsis-dist-nocleanup: nsis-dist-prepare
nsis-dist-nocleanup: win-dist-prepare
cd nsis-dist && @NSIS_EXEC@ gsmartcontrol.nsi
mv nsis-dist/$(PACKAGE)-$(VERSION)-@WINDOWS_SUFFIX@.exe .
mv nsis-dist/$(PACKAGE)-$(VERSION).exe .
nsis-dist-cleanup:
nsis-cleanup:
rm -rf nsis-dist
nsis-dist: nsis-dist-prepare nsis-dist-nocleanup nsis-dist-cleanup
win-zip-dist: win-zip-dist-nocleanup win-zip-dist-cleanup
win-zip-dist-prepare: win-dist-prepare
cp -a win-dist $(WIN_ZIP_NAME)
win-zip-dist-nocleanup: win-zip-dist-prepare
win-zip-dist-nocleanup: win-dist-prepare
mv nsis-dist $(WIN_ZIP_NAME)
rm -rf $(WIN_ZIP_NAME)/gsmartcontrol.ico $(WIN_ZIP_NAME)/nsi_* $(WIN_ZIP_NAME)/*.nsi
zip -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME)
# Only when cross-compiling:
# zip -K -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME)
win-zip-dist-cleanup:
rm -rf $(WIN_ZIP_NAME)
win-zip-dist: win-zip-dist-prepare win-zip-dist-nocleanup win-zip-dist-cleanup
distclean-local: win-dist-clean
distclean-local: gch-clean win-dist-clean
-161
View File
@@ -1,164 +1,3 @@
Version 1.1.4, released on 2022-02-04
Fixed a crash when trying to render main window icons which have incomplete
data; this fixes a rare crash when scanning devices (#13).
Fixed rendering compatibility issues with newer GTK3 versions.
Fixed build system conflict with C++20 version header (#14).
Fixed "Running command ..." dialog floating above windows of other
applications (#17).
Fixed a crash when system locale is set to invalid locale.
Support Finnish decimal separator (backported from the Debian package)
(#23).
Set LC_NUMERIC=C for smartctl process to avoid locale-dependent number
formatting (#23).
Fixed printing all GTK messages as warnings; the messages are sorted by
severity now.
Add current time to default filename in Save Output dialog.
Support macos stat command in file2csource.sh.
Fixed a few typos (backported from the Debian package).
Windows port now uses Adwaita theme due to issues with win32 theme.
Windows port is dpi-aware now.
Version 1.1.3, released on 2017-11-12
Fixed gsmartcontrol-root not launching if GDK_* variables are not set.
Version 1.1.2, released on 2017-11-11
Fixed gsmartcontrol-root not passing GDK_SCALE and GDK_DPI_SCALE variables
to gsmartcontrol when using PolKit.
Fixed blurriness of icons in the main window with GDK_SCALE=2.
Tweaked the main window interface.
Windows: Show volume labels beside drive letters in icon tooltips.
Version 1.1.1, released on 2017-09-25
Windows: Use Adwaita GTK+ theme for systems which support Classic Windows
theme, since the default win32 GTK+ theme is broken in it; this
includes Windows 7 and Windows Server.
Statistics entry values are formatted with commas for readability.
Moved help information to website.
Version 1.1.0, released on 2017-09-07
New Statistics, Temperature Log, Error Recovery, Physical and Directory
tabs.
General tab shows non-SMART device settings as well.
Attributes tab shows entries in "brief" format.
Error Log tab shows Extended error log by default (if supported).
Self-Test Log tab now shows Extended self-test log by default (if
supported).
GSmartControl now uses "-x" equivalent for retrieving data (as upposed to
"-a"); loading "-x" outputs as virtual drives is also supported.
Implemented ability to copy rows in CSV format from Attribute, Statistics
and Self-Test Log tables.
Implemented "Update Drive Database" functionality.
Windows: Drive letters are shown for each drive.
Scan time is shown under virtual drive icons.
Polkit is supported with gsmartcontrol-root script now.
Pcrecpp is no longer bundled, use system-installed one instead.
Smartmontools version 5.43 is required at runtime.
Other minor improvements and fixes.
Version 1.0.2, released on 2017-07-21
Fixed incomplete capturing of smartctl output under Windows.
Added missing icons under Windows.
Fixed being able to turn on AODC even if unsupported.
Version 1.0.1, released on 2017-06-19
Fixed compilation under macOS.
Fixed compilation under Fedora Rawhide.
Don't use -mtune=generic for all targets.
Version 1.0.0, released on 2017-06-16
Ported to GTK+ 3.
Tweaked the user interface a bit.
Dropped support for Windows XP, 2000 and 2003 (they are no longer
supported by GTK+).
Fixed detection of newer system-installed smartmontools under Windows.
Version 0.9.0, released on 2017-05-11
Implemented (untested) support for Linux-based Areca controllers with
enclosures.
Implemented (untested) support for Windows-based Areca controllers (thanks
to Richard Kagerer).
Implemented (untested) support for Linux-based HP controllers with cciss
and hpsa/hpahcisr drivers (thanks to Fabrice Bacchella).
Changes in Preferences no longer fail silently until rescan/restart.
Better drive detection under Windows after removable drives are detached.
Windows version is no longer marked as "dpi aware" since it's not
supported that well.
Drive attribute descriptions have been updated (including clarifications
for SSDs).
Added support for SSD-only and HDD-only vendor attributes.
Devices having only basic info can be displayed now in the info window.
Fixed BDRW drive detection (it was detected as a HDD).
Other minor improvements.
A number of issues have been fixed (including a crash).
Version 0.8.7, released on 2012-08-11
Implemented support for Adaptec RAID 5805 controller and possibly other
Adaptec models as well (Linux).
Implemented support for Areca RAID controller detection under Linux
(untested).
Implemented support for 3ware 3w-sas-supported (twl) RAID controller
detection under Linux (untested).
Implemented support for systems with several different 3ware controllers
(twa/twe/twl).
Fixed invalid parsing of tw_cli output which caused non-detection of
drives and controllers with controller or port numbers greater than 9
if tw_cli was found (3ware linux, windows).
The duplicate drives are no longer shown for some Intel matrix controllers
under Windows; Intel RAID controllers are fully supported under
Windows now.
Added options to show device name and serial number under drive icons.
Updated SMART attribute definitions and added warnings for SSD lifetime
attributes.
gsmartcontrol-root has a new argument "--desktop=..." which replaces the
old positional argument; compatibility with the old invocation syntax
has been retained.
Completely documented the source using doxygen tags.
Added support for Fedora's consolehelper.
Made GSmartControl DPI-aware under Windows.
Fixed compilation issues with clang and gcc 4.7, as well as glib 2.31.x
and newer.
Fixed minor issues in NSIS installer.
Fixed other minor bugs and made minor improvements.
Version 0.8.6, released on 2011-06-12
Support detecting drives behind 3ware controllers (Linux, Windows),
including tw_cli/cx/px mode in Windows. Having tw_cli is recommended
but not required.
Added support for specifying -d option and extra parameters via command
line, "Add Device" and "Preferences" dialogs. This change effectively
adds full support for multiple drives behind a single device name.
Beesu and su-to-root are supported by gsmartcontrol-root script now.
Completely revamped the attribute database and its handling, should be a
lot more usable and forward-compatible now; SSD attributes are also
included.
In-program help has been expanded considerably.
General improvements to user interface have been implemented (better GNOME
HIG compliance, better tooltips, dialogs, etc...).
Added ability to show smartctl output for devices whose info could not be
parsed fully.
Attributes in "brief" format are supported now.
The parser has been updated to reflect the recent changes in smartctl.
Quit and rescan operations are no longer denied without confirmation when
tests are running.
The Windows NSIS installer has been vastly improved.
Better support for Windows Vista and 7.
Changed copyright notices for files with Whatever License to use Unlicense.
Added other minor features and fixed quite a few bugs.
Version 0.8.5, released on 2009-09-05
GSmartControl now uses XDG config directory for per-user configuration on
UNIX and CSIDL_PROFILE directory on Windows. Existing configuration is
migrated automatically.
The names are shown correctly for unsupported devices even with the latest
smartctl snapshots now.
Smartctl SVN revision is shown (if available).
The progress bars update properly when parallel tests are run.
Windows: GSmartControl should be able to operate on any valid filesystem
path (not just locale-representable ones).
Windows: GSmartControl is now officially compilable on x86_64 via mingw64.
Fixed compilation under very old gtkmm/libglademm, and with gcc 4.4.
Fixed parsing of multiple error types in SMART error log.
Added minor features and fixed miscellaneous bugs.
Version 0.8.4, released on 2009-03-23
Linux Software RAID devices are blacklisted now. (backported from Debian).
+360 -49
View File
@@ -1,92 +1,218 @@
About GSmartControl
GSmartControl - Hard disk drive and SSD health inspection tool.
GSmartControl - Hard disk drive health inspection tool.
GSmartControl is a graphical user interface for smartctl (from smartmontools
package, see https://www.smartmontools.org/), which is a tool for
package, see http://smartmontools.sourceforge.net), which is a tool for
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
Technology) data on modern hard disk and solid-state drives. It allows you to
inspect the drive's SMART data to determine its health, as well as run various
tests on it.
Technology) data on modern hard disk drives. It allows you to inspect the
drive's SMART data to determine its health, as well as run various tests on
it.
GSmartControl supports ATA drives (both PATA and SATA), various USB to
ATA bridges and drives behind some RAID controllers:
* Adaptec (Linux, some models only)
* Areca (Linux, Windows)
* HP CCISS (Linux)
* HP hpsa / hpahcisr (Linux)
* Intel Matrix Storage (CSMI) (Linux, Windows, FreeBSD)
* LSI 3ware (Linux, Windows)
* LSI MegaRAID (Windows)
Note: Only ATA drives (both PATA and SATA) are supported for now.
Note: Smartmontools supports even more RAID Controllers. The drives
behind such controllers can be manually added to GSmartControl using
"Add Device..." functionality or --add-device command-line option.
See https://www.smartmontools.org/wiki/Supported_RAID-Controllers .
http://gsmartcontrol.berlios.de
https://gsmartcontrol.shaduri.dev
Features
* automatically reports and highlights any anomalies;
* allows enabling/disabling SMART;
* allows enabling/disabling Automatic Offline Data Collection - a short
self-check that the drive will perform automatically every four hours with no
impact on performance;
* supports configuration of global and per-drive options for smartctl;
* displays drive identity information, capabilities, attributes, and
self-test/error logs;
* performs SMART self-tests;
* can read in smartctl output from a saved file, interpreting it as a
read-only virtual device;
* works on most smartctl-supported operating systems;
* has extensive help information.
What Is SMART?
Short answer: SMART is a technology which provides hard disk drives with
methods to predict certain kinds of failures with certain chance of success.
Long answer: read below.
Self-Monitoring, Analysis, and Reporting Technology, or SMART, is a
monitoring system for hard drives to detect and report various indicators of
reliability, in the hope of anticipating failures. It is implemented inside
the drives SMART provides several ways of monitoring hard drive health. It
may provide information about general health, various drive attributes (for
example, number of unreadable sectors), error logs, and so on. It may also
provide ways to instruct the drive to run various self-tests, which may report
valuable information. It may even automatically scan the disk surface in when
the drive is idle, and repair the defects, reallocating the data to more safe
areas.
While having SMART sounds really good, there are some nuances to consider. One
of the common pitfalls is that it may create a false sense of security. That
is, a perfectly good SMART data is NOT an indication that the drive won't fail
the next minute. The reverse is also true - some drives may function perfectly
even with not-so-good-looking SMART data. However, as studies indicate, given
a large population of drives, some SMART attributes may reliably predict
drive failures within up to two months.
Another common mistake is to assume that the attribute values are the real
physical values, as experienced by the drive. As manufacturers do not
necessarily agree on precise attribute definitions and measurement units, the
exact meaning of the attributes may vary greatly across different drive
models.
At present SMART is implemented individually by manufacturers, and while some
aspects are standardized for compatibility, others are not. In fact, most
manufacturers refer the users to their own health monitoring utilities, and
advice against taking SMART data seriously. Nevertheless, SMART may prove an
effective measure against data loss.
Yet another issue is that quite often the drives have bugs which prevent
correct SMART usage. This is usually due to buggy firmware, or the
manufacturer ignoring the standard. Luckily, smartmontools usually detects
these bugs and works around them.
Software Requirements
Note: If using the official Windows package, no additional software is required.
You need to have the following software installed:
* pcre - http://www.pcre.org .
* smartmontools - see http://smartmontools.sourceforge.net .
* GTK+, version 2.6 or higher - see http://www.gtk.org .
* Gtkmm, version 2.6 or higher - see http://www.gtkmm.org .
* libglademm, version 2.4 or higher - see http://www.gtkmm.org .
Note: libglademm is not needed when using GTK 2.12 and Gtkmm 2.12.
Note that having GTK+ 2.12 and Gtkmm 2.12 is HIGHLY recommended. While earlier
versions may work, they may produce suboptimal results and buggy behavior.
Libglademm is not needed when using GTK / Gtkmm 2.12.
Most of these packages are probably already provided by your distribution.
Here are the packages you need to have to build GSmartControl on some Linux
distributions:
openSUSE, SLES, SLED:
gcc-c++, pcre-devel, gtkmm2-devel, (possibly) libglademm-devel.
Fedora, CentOS, RHEL:
gcc-c++, pcre-devel, gtkmm24-devel, (possibly) libglademm24-devel.
Mandriva:
gcc-c++, gcc-cpp, libpcre-devel, gtkmm2.4-devel, (possibly)
libglademm2.4-devel.
Ubuntu, Debian GNU/Linux:
g++, libpcre3-dev, libgtkmm-2.4-dev, (possibly) libglademm-2.4-dev.
Note that usually you need to specify only these packages - the rest is
installed automatically by the package manager's dependency resolver. Keep in
mind that you also need smartmontools to run the program.
Note: If using the official Windows package, the requirements are listed on
GSmartControl's website.
Build requirements:
* pcre 1 - http://www.pcre.org .
* GTK+, version 3.4 or higher - see http://www.gtk.org .
* Gtkmm, version 3.4 or higher - see http://www.gtkmm.org .
Runtime requirements:
* smartmontools - see https://www.smartmontools.org/ .
* xterm
The following operating systems are supported:
* Linux - All the popular configurations should work.
* FreeBSD - Tested with DesktopBSD / x86.
* NetBSD - Tested with NetBSD / x86.
* OpenBSD - Tested with OpenBSD / x86-64.
* FreeBSD - Tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86.
* NetBSD - Tested with NetBSD 4.0.1 / x86.
* OpenBSD - Tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5.
* Windows (2000 SP4 or newer) - Tested with Windows 2000 SP4, XP SP2 and Vista
SP1. The Windows port uses pd0, pd1, etc... for physical drives 0, 1, etc... .
* Solaris - Tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave,
Solaris 10 / x86 / sunstudio12 / sunfreeware. OpenSolaris should work but has
not been tested yet. Note that until either smartctl gets ATA support under
Solaris or GSmartControl gets SCSI support, these configurations are
essentially useless.
* Mac OS X - 3rd-party testing by Fink project.
* DragonFlyBSD - Code written but no testing has been performed yet. Expected
to work without any issues.
* Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1, Windows Server 2008,
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
for physical drives 0, 1, etc... .
* Mac OS X.
* Solaris.
to work without any issues.
* QNX - Code written but no testing has been performed yet.
Installation
Short answer: build and install via: ./configure; make; make install
Run gsmartcontrol-root to invoke gsmartcontrol with your desktop's su
mechanism, or use the desktop menu entry.
Long answer: read below.
First, check if you can find a pre-built package for your distribution or
operating system - they usually provide the best integration and the easiest
installation procedure. For Linux, one option is to try the openSUSE Build
Service - it provides ready-to-install packages for various popular Linux
distributions (openSUSE, Fedora, etc...). See
http://download.opensuse.org/repositories/home:/alex_sh/ .
If you want to compile from source, check that you have all the required
dependencies (see Software Requirements section). Then the usual
./configure; make; make install
will build and install it. Installation usually requires administrative
privileges, but you don't need to install the program in order to run it
directly from the build directory.
Command Line Options
GSmartControl inherits options from GTK+ and other libraries, so be sure to
run it with --help option to get a full list of accepted parameters.
Note: The Windows version may not have a text output at all, so --help and
similar arguments won't have any effect.
similar arguments won't have any effect. However, --verbose will still
increase the verbosity of a log saved via "Options -> View Execution Log ->
Save All".
The most important parameters are:
-?, --help - Show help options.
-l, --no-locale - Don't use system locale.
-V, --version - Display version information.
-l, --no-locale - Disable locale.
--no-scan - Don't scan devices on startup.
--no-hide-tabs - Don't hide non-identity tabs when SMART is disabled. Useful
for debugging.
--no-hide-tabs - Don't hide non-identity tabs when SMART is disabled.
--add-virtual - Load smartctl data from file, creating a virtual drive. You
can specify this option multiple times.
--add-virtual - Load smartctl data from file, creating a virtual drive.
--add-device - Add this device to device list. The format of the device is
"<device>::<type>::<extra_args>", where type and extra_args are optional. This
option is useful with --no-scan to list certain drives only. You can specify
this option multiple times.
Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
'/dev/sdb::::-T permissive'.
--add-device - Add this device to device list. Useful with --no-scan to list
certain drives only.
-v, --verbose - Enable verbose logging; same as --verbosity-level 5.
@@ -96,6 +222,191 @@ Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
Permission Problems
Short answer: you need to be root (that's Administrator in Windows).
In X11, use kdesu, gnomesu, sux, xdg-su or similar.
Long answer: read below.
Most operating systems prohibit direct access to hardware to users with
non-administrative privileges. Unfortunately, to access SMART data, smartctl
needs to directly access the hard drive.
The provided X11 desktop and menu icons should show the "Please enter the root
password" dialog boxes, and, after correct information is entered, should run
this program with root privileges. The dialogs should be available in most
commonly used X11 desktop environments.
Another way is to use the included gsmartcontrol-root script, which finds the
available su program and runs gsmartcontrol with it.
Yet another way is to manually invoke the program with kdesu, gnomesu, sux or
similar programs. For example,
kdesu -u root -c gsmartcontrol
will ask for root password and run gsmartcontrol with root privileges. Replace
"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also
help, if neither KDE or GNOME are available.
Please don't set the "setuid" flag on smartctl binary. It is considered a
security risk.
Enable SMART Permanently
Specifications say that once you set a SMART-related property, it will be
preserved across reboots. So, when you, say, enable SMART and Automatic
Offline Data Collection, both will stay enabled until you disable them.
However, BIOS, your operating system, your other operating systems (if
present), and various startup programs may affect that. For example, BIOS may
enable SMART each time you start your computer, so if you disabled SMART
previously, it will be re-enabled on reboot.
The easiest way to work around this is to set the desired settings on system
startup. You may use smartctl or smartd to do that. For example, to enable
both SMART and Automatic Offline Data Collection on /dev/sda, one would write
the following to the system startup script (e.g. boot.local, rc.local or
similar on Linux):
smartctl -s on -o on /dev/sda
For more information, see smartctl and smartd documentation.
Known Issues
Only ATA (both PATA and SATA) disks are supported for now. The main reasons
are:
* We can't support drives which don't work with smartmontools. This affects
drives which don't support SMART or don't export SMART data correctly (e.g.
USB enclosures, RAIDs, etc...).
* Smartctl's output for SCSI drives is completely different compared to ATA.
Also, SCSI drives are rarely found in desktop systems and the servers rarely
have X11 / Gtkmm running, so this is a low priority task.
* I only have ATA drives, so testing would be almost impossible.
Immediate Offline Tests are not supported. I haven't found a way to reliably
monitor them yet. Besides, they run automatically anyway if Automatic Offline
Data Collection is enabled.
You may run several tests on different drives in parallel, but you will only
see the progress information of the last executing test. So, if you run tests
on drives A, B and C (in that order), you won't be able to monitor the tests
on A and B until C completes. And you won't be able to see any progress on A
until both B and C complete their tests. This doesn't affect the tests
themselves, just your ability to see their progress.
Testing is only supported on drives which correctly report their progress
information in capabilities.
Not all drives support disabling Automatic Offline Data Collection, even if
they report otherwise.
Running on GTK+ / Gtkmm versions earlier than 2.12 may cause visual artifacts,
usability issues (especially with tooltips and icons), instability, etc...
The texts probably contain a lot of grammatical errors, English being my third
language and all.
Reporting Bugs
If it is a SMART or drive-related problem, please try to test it with smartctl
first. Chances are, the problem you're experiencing is not tied to
GSmartControl, but is a drive firmware or smartctl problem. For example, to
see a complete information about your /dev/sda drive, type the following in a
terminal emulator (e.g., xterm, konsole or gnome-terminal):
smartctl -a /dev/sda
Note: If using Windows, the device name should be /dev/pd1 for the second
physical drive, etc... .
If you still think it's a GSmartControl issue, please collect the following
information about your system. Without it, it may be very hard or impossible
to fix the bug.
* Which operating system you use (for example, openSUSE Linux 11.0).
* Which version of GTK and Gtkmm you have installed. Finding this out is very
distribution-specific. For example, on openSUSE it would be
"rpm -q gtk2 gtkmm2". Some distributions have gtkmm24 instead. You may also
search them in your distribution's graphical package manager, if there is one.
* Execution log from the program, if possible. To obtain it, run the program
with -v option, e.g. (type the following in a terminal emulator or Run
dialog):
gsmartcontrol-root auto -v
Note: On Windows, run gsmartcontrol.exe with "-v" switch as Administrator.
Perform the steps needed to reproduce the bug, then go to
"Options -> View Execution Log", and click "Save All".
* Detailed description of steps you performed when the bug occurred.
Once you have this information, send an email to me, Alexander Shaduri
<ashaduri 'at' gmail.com>. Note that I may refer you to smartmontools support
if it's a bug in smartmontools and not GSmartControl. Normally, I won't
redirect your support request to them myself, because they may ask questions
which only you have the answers to.
Please read the "License and Copyright" and "Patch Licensing" sections before
sending any patches.
License and Copyright
For license information, see LICENSE_gsmartcontrol.txt file.
You may notice that GSmartControl is not licensed under "GNU GPL version X or
later", but under "GNU GPL version X and Y". I firmly believe that it's unwise
to license a piece of code under non-existent licenses, whatever anyone else
might say. The reason for this is that one simply CANNOT know that, say, in 20
years FSF won't be bought by some corporation who will release GPL version Z
which will completely reverse the reasons GPL was created for.
The removal of "or later" clause somewhat imposes responsibility on the
copyright holders to review every future version of the license once it's
released, and, if deemed acceptable, re-license the code under the new license
(possibly retaining the old licenses). Unfortunately, this is a necessary
inconvenience we will have to deal with.
Patch Licensing
Due to reasons described in "License and Copyright" section, to make it
possible to re-license the code without tracking down all the people who ever
wrote a patch, the copyrights of all minor patches must be assigned to the
central copyright holder of the project. If the patch is major enough (that
is, it forms a significant part of the program source code), the author may
retain the copyright, if he or she chooses to do so. However, unless the
author plans to maintain his part of the source code, he / she is humbly asked
to consider assigning away his / her copyright. A simple "I disclaim all
copyright to this patch" by the author is sufficient. All credits will be
mentioned in product documentation, whatever the size of the patch is.
Please note that centralization of copyright is needed to maintain a
reasonably healthy legal status of the project. Also note that this method is
not unique - FSF and many other organizations require exactly the same thing.
Some contributors may have reasonable doubts about the future status of this
project. Let me assure you that this project will never have more restrictive
license than GPLv2. If, some time in the future, the GPL is somehow
invalidated in court, the project will be re-licensed under similar (in
spirit) license, if possible, or a license less restrictive than GPL (for
example, the three-clause BSD license).
+121 -19
View File
@@ -15,35 +15,137 @@ local scope destruction also works if using stack variables.
}
A treeview (also iconview, combobox) from glade comes
with existing model, which should be unset.
--------------------------------------------------------
portability:
win32 32-bit: int: 4, long: 4, pointer: 4.
win32 64-bit: int: 4, long: 8, pointer: 8.
sparc: same as win32 (what about OS?).
--------------------------------------------------------
TODO:
-l defects support? (smartctl 6.6)
Patch pangomm for Pango::strip_markup().
Patch gtkmm, glibmm, pangomm for static win32 builds.
No Gtk::AboutDialog::set_logo(NULL);
Don't rely on smartctl return code (2), parse the output instead.
This will allow us to show the Info page.
Need usage cases.
libdebug:
per-channel disabling.
per-channel formatting.
all flags should be per-channel.
bug: if -q is passed, Execution log doesn't contain libdebug log. (requires per-channel status control).
Testing:
If ETA time has elapsed, but it's still only at 10% completion,
ETA 0 is displayed. Fix.
Detect running tests on launch (maybe ask the user too? some tests
may be stuck due to bad firmware, e.g. 3ware/windows).
If smartctl outputs gibberish while testing, the GUI hangs.
'-l sataphy' -> SATA Phy event counters: Non-SMART feature, useful to detect SATA cabling problems.
'-l xerror' -> Extended Comprehensive Error log: An multi-sector and
LBA48 capable version of '-l error'. Output is similar, except more
entries can exists and the high bytes of the registers are also printed.
Some newer drives (Samsung) only write to this log.
'-l scttemp' -> SCT Temperature history table. See below.
'-l xselftest' -> Extended Self Test Log: An multi-sector and LBA48
capable version of '-l selftest'. Output will be similar, except more
entries can exists and the LBA values can be somewhat longer.
Support RAID for these controllers (supported by smartctl 6.5):
https://www.smartmontools.org/wiki/Supported_RAID-Controllers
Adaptec (Windows, Linux using "-d aacraid")
HighPoint RocketRAID (Linux, FreeBSD (analyze sent info))
LSI MegaRAID (Linux, FreeBSD)
3ware (FreeBSD)
Areca (FreeBSD)
HP CCISS (FreeBSD)
----------- Feature List ----------
+Autodetect available devices
+Select current device (from autodetected list; manually whitelist devices).
+Smartctl binary (text entries in Options - binary, default command line options).
+ Global command-line options for smartctl.
+ Command-line options per-device in settings (e.g. -d ata).
+ Search smartctl binary on startup (try to execute it with --version). If it's not there, alert the user.
+Indicate which of the devices has SMART supported / enabled.
+ Enable / disable smart on devices.
+ Indicate that this is until shutdown (but may be preserved on reboot).
+ Direct them to smartd / smartctl documentation for permanent enabling.
+ NOTE: SMART is sometimes preserved across power cycles.
+Enable / disable Automatic Offline Data Collection for devices. (-o on)
+ Show its status in the main window.
+Show identity information for device (aka short info), --info.
+Show all SMART information for device (--all), includes:
+ * Short info (--info)
+ * SMART DATA
+ * overall-health (-H, --health) - may check this automatically as well.
+ * General SMART Values, aka Capabilities (-c, --capabilities)
+ * Attributes (-A, --attributes). These need decoding.
+ * Error Log (-l error)
+ * Self-test log (-l selftest)
+ * Selective self-test log and settings
* SCT status and other stuff
Display temperature somewhere (use SCT or Attr 194 for this).
+ * Ability to save this information to a file.
+ * On info window show: Ask to turn SMART on if it's disabled
+Program error reporting:
+ invalid smartmontools version.
+ cannot parse output - show log.
+ smartctl exited with error - look it up in error map.
SCT Temperatures
-l scttemp prints temperature and history.
-t scttempint,N[,p] (N - count, p - preserve across reset or not).
This configures temperature logging interval (1 min by default) and clears the history.
+Tests:
Ability to detect a testing drive on boot, show its info with testing tab.
Run one-time Immediate Offline test (-t offline).
Offline tests only update Attributes, and if errors are found they will
appear in SMART error log. Self-test logs are unaffected.
Abort (-X). Abort works with Offline only if there's
"Abort Offline collection upon new command" capability.
If the drive has "Suspend Offline collection upon new command" capability,
immediate offline test may be tracked through --capabilities (not on mine!).
If it's "Abort Offline collection upon new command", then the test
will abort on --capabilities or --abort.
Polling time
The self-test routine recommended polling time shall be equal to the number of minutes that is the minimum
recommended time before which the host should first poll for test completion status. Actual test time could
be several times this value. Polling before this time could extend the self-test execution time or abort the test
depending on the state of bit 2 of the off-line data capability bits.
Autosave
This command enables and disables the optional attribute autosave feature of the device. This command
may either allow the device, after some vendor specified event, to save the device updated attribute values
to non-volatile memory; or this command may cause the autosave feature to be disabled. The state of the
attribute autosave feature (either enabled or disabled) shall be preserved by the device across power cycles.
A value of zero written by the host into the device’s Sector Count register before issuing this command shall
cause this feature to be disabled. Disabling this feature does not preclude the device from saving SMART
data to non-volatile memory during some other normal operation such as during a power-on or power-off
sequence or during an error recovery sequence.
A value of F1h written by the host into the device’s Sector Count register before issuing this command shall
cause this feature to be enabled. Any other meaning of this value or any other non-zero value written by the
host into this register before issuing this command may differ from device to device. The meaning of any
non-zero value written to this register at this time shall be preserved by the device across power cycles.
+8 -19
View File
@@ -26,23 +26,12 @@
AC_DEFUN([AC_CXX_EXCEPTIONS],
[AC_CACHE_CHECK(whether the compiler supports exceptions,
ac_cv_cxx_exceptions,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
[[try { throw 1; } catch (int i) { return i; }]])],
[ac_cv_cxx_exceptions=yes], [ac_cv_cxx_exceptions=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_exceptions" = yes; then
AC_DEFINE(HAVE_EXCEPTIONS, 1,
[defined to 1 if the compiler supports exceptions, 0 otherwise])
else
AC_DEFINE(HAVE_EXCEPTIONS, 0,
[defined to 1 if the compiler supports exceptions, 0 otherwise])
fi
ac_cv_cxx_exceptions,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[try { throw 1; } catch (int i) { return i; }]])],[ac_cv_cxx_exceptions=yes],[ac_cv_cxx_exceptions=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_exceptions" = yes; then
AC_DEFINE(HAVE_EXCEPTIONS,,[define if the compiler supports exceptions])
fi
])
+8 -18
View File
@@ -26,22 +26,12 @@
AC_DEFUN([AC_CXX_NAMESPACES],
[AC_CACHE_CHECK(whether the compiler implements namespaces,
ac_cv_cxx_namespaces,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[namespace Outer { namespace Inner { int i = 0; }}]],
[[using namespace Outer::Inner; return i;]])],
[ac_cv_cxx_namespaces=yes], [ac_cv_cxx_namespaces=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_namespaces" = yes; then
AC_DEFINE(HAVE_NAMESPACES, 1,
[defined to 1 if the compiler implements namespaces, 0 otherwise])
else
AC_DEFINE(HAVE_NAMESPACES, 0,
[defined to 1 if the compiler implements namespaces, 0 otherwise])
fi
ac_cv_cxx_namespaces,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[namespace Outer { namespace Inner { int i = 0; }}]], [[using namespace Outer::Inner; return i;]])],[ac_cv_cxx_namespaces=yes],[ac_cv_cxx_namespaces=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_namespaces" = yes; then
AC_DEFINE(HAVE_NAMESPACES,,[define if the compiler implements namespaces])
fi
])
+21 -31
View File
@@ -26,35 +26,25 @@
AC_DEFUN([AC_CXX_RTTI],
[AC_CACHE_CHECK(whether the compiler supports Run-Time Type Identification,
ac_cv_cxx_rtti,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <typeinfo>
class Base {
public:
Base () {}
virtual int f () { return 0; }
};
class Derived : public Base {
public :
Derived () {}
virtual int f () { return 1; }
};
]], [[
Derived d;
Base* ptr = &d;
return typeid (*ptr) == typeid (Derived);
]])],
[ac_cv_cxx_rtti=yes], [ac_cv_cxx_rtti=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_rtti" = yes; then
AC_DEFINE(HAVE_RTTI, 1,
[defined to 1 if the compiler supports Run-Time Type Identification, 0 otherwise])
else
AC_DEFINE(HAVE_RTTI, 0,
[defined to 1 if the compiler supports Run-Time Type Identification, 0 otherwise])
fi
ac_cv_cxx_rtti,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <typeinfo>
class Base { public :
Base () {}
virtual int f () { return 0; }
};
class Derived : public Base { public :
Derived () {}
virtual int f () { return 1; }
};
]], [[Derived d;
Base *ptr = &d;
return typeid (*ptr) == typeid (Derived);
]])],[ac_cv_cxx_rtti=yes],[ac_cv_cxx_rtti=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_rtti" = yes; then
AC_DEFINE(HAVE_RTTI,,
[define if the compiler supports Run-Time Type Identification])
fi
])
+12 -21
View File
@@ -25,25 +25,16 @@
AC_DEFUN([AC_CXX_VERBOSE_TERMINATE_HANDLER],
[AC_CACHE_CHECK(whether the compiler has __gnu_cxx::__verbose_terminate_handler,
ac_cv_verbose_terminate_handler,
[
AC_REQUIRE([AC_CXX_EXCEPTIONS])
AC_REQUIRE([AC_CXX_NAMESPACES])
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[#include <exception>]],
[[std::set_terminate(__gnu_cxx::__verbose_terminate_handler);]])],
[ac_cv_verbose_terminate_handler=yes], [ac_cv_verbose_terminate_handler=no])
AC_LANG_POP([])
])
if test "$ac_cv_verbose_terminate_handler" = yes; then
AC_DEFINE(HAVE_VERBOSE_TERMINATE_HANDLER, 1,
[defined to 1 if the compiler has __gnu_cxx::__verbose_terminate_handler, 0 otherwise])
else
AC_DEFINE(HAVE_VERBOSE_TERMINATE_HANDLER, 0,
[defined to 1 if the compiler has __gnu_cxx::__verbose_terminate_handler, 0 otherwise])
fi
ac_cv_verbose_terminate_handler,
[
AC_REQUIRE([AC_CXX_EXCEPTIONS])
AC_REQUIRE([AC_CXX_NAMESPACES])
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <exception>]], [[std::set_terminate(__gnu_cxx::__verbose_terminate_handler);]])],[ac_cv_verbose_terminate_handler=yes],[ac_cv_verbose_terminate_handler=no
])
AC_LANG_POP([])
])
if test "$ac_cv_verbose_terminate_handler" = yes; then
AC_DEFINE(HAVE_VERBOSE_TERMINATE_HANDLER, , [define if the compiler has __gnu_cxx::__verbose_terminate_handler])
fi
])
+1 -1
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
+153
View File
@@ -0,0 +1,153 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_CHOOSE_GLD_READER([C|C++][, flags_prefix])
# This macro detects whether GtkBuilder (from gtk >= 2.12) or libglade
# are present. If the first parameter is C++, then gtkmm and libglademm
# are searched for instead.
# flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLIBS
# will be initialized (or appended to) with the correct flags in case of libglade(mm).
# app_cv_gld_reader will be set to none, libglade or gtkbuilder.
# ENABLE_LIBGLADE or ENABLE_GTKBUILDER will be exported to config.h
# You must call AX_COMPILER_VENDOR before using this macro.
AC_DEFUN([APP_CHOOSE_GLD_READER], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_ARG_ENABLE(abort-if-no-glade-reader, AS_HELP_STRING([--disable-abort-if-no-glade-reader],
[disable abort if no glade file reader is found (default: configure aborts when glade file reader is not found)]),
[app_cv_abort_if_no_glade_reader=${enableval}], [app_cv_abort_if_no_glade_reader=yes])
AC_MSG_NOTICE([Abort if no glade file reader is found: $app_cv_abort_if_no_glade_reader])
# This is just in case
# PKG_PROG_PKG_CONFIG()
# this variable will have values "none", "libglade" or "gtkbuilder".
app_cv_gld_reader="none";
# in case libglade(mm) is found, these will contain the flags.
app_cv_gld_reader_CFLAGS="";
app_cv_gld_reader_CXXFLAGS="";
app_cv_gld_reader_LIBS="";
app_cv_gld_reader_libglade_name="libglade";
app_cv_gld_reader_libglade_pkg_name="libglade-2.0";
app_cv_gld_reader_gtk_name="gtk";
app_cv_gld_reader_gtk_pkg_name="gtk+-2.0";
if test "x$1" = "xC++"; then
app_cv_gld_reader_libglade_name="libglademm";
app_cv_gld_reader_libglade_pkg_name="libglademm-2.4";
app_cv_gld_reader_gtk_name="gtkmm";
app_cv_gld_reader_gtk_pkg_name="gtkmm-2.4";
fi
# Manual control. AC_ARG_ENABLE doesn't get bash variables, so we have
# to do it this way. If put inside a shell conditional, it still shows up in configure help.
AC_ARG_ENABLE(libglade, AS_HELP_STRING([--enable-libglade],
[use libglade(mm) instead of GtkBuilder (default: auto)]),
[with_libglade=${enableval}], [with_libglade=auto])
# Try gtkbuilder first
if test "x$with_libglade" = "xno" || test "x$with_libglade" = "xauto"; then
PKG_CHECK_EXISTS([$app_cv_gld_reader_gtk_pkg_name >= 2.12.0],
[app_cv_gld_reader="gtkbuilder"])
fi
if test "$with_libglade" = "no" && test "$app_cv_gld_reader" = "none"; then
AC_MSG_ERROR([GtkBuilder support not found (you need $app_cv_gld_reader_gtk_name version 2.12 or higher). Try using $app_cv_gld_reader_libglade_name instead.])
fi
# libglade specified, or auto and no gtkbuilder. gtkmm2.4-compatible libglademm is >= 2.4.0.
# (not sure about libglade version).
if test "x$app_cv_gld_reader" = "xnone"; then
if test "x$app_cv_gld_reader_libglade_name" = "xlibglade"; then
# libglade
PKG_CHECK_MODULES([LIBGLADE], [$app_cv_gld_reader_libglade_pkg_name >= 2.4.0],
[app_cv_gld_reader="libglade"], [AC_MSG_WARN([$LIBGLADE_PKG_ERRORS])])
app_cv_gld_reader_CFLAGS="$LIBGLADE_CFLAGS";
app_cv_gld_reader_CXXFLAGS="$LIBGLADE_CFLAGS";
app_cv_gld_reader_LIBS="$LIBGLADE_LIBS";
else
# libglademm
PKG_CHECK_MODULES([LIBGLADEMM], [$app_cv_gld_reader_libglade_pkg_name >= 2.4.0],
[app_cv_gld_reader="libglade"], [AC_MSG_WARN([$LIBGLADEMM_PKG_ERRORS])])
app_cv_gld_reader_CFLAGS=""
app_cv_gld_reader_CXXFLAGS="$LIBGLADEMM_CFLAGS";
app_cv_gld_reader_LIBS="$LIBGLADEMM_LIBS";
fi
# reset them so they don't interfere later
LIBGLADE_CFLAGS="";
LIBGLADE_LIBS="";
fi
if test "x$with_libglade" = "xyes" && test "x$app_cv_gld_reader" = "xnone"; then
AC_MSG_ERROR([$app_cv_gld_reader_libglade_name not found. Try building with GtkBuilder support (you need $app_cv_gld_reader_gtk_name version 2.12 or higher).])
fi
# still not found.
if test "$app_cv_gld_reader" = "none"; then
if test "x$app_cv_abort_if_no_glade_reader" = "xno"; then
AC_MSG_WARN([$LIBGLADE_PKG_ERRORS])
AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found. The program will be unable to compile fully.])
else
AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found.])
AC_MSG_ERROR([$LIBGLADE_PKG_ERRORS])
fi
fi
# export the flags
if test "x$2" != "x"; then
if test "x$$2[]CFLAGS" = "x"; then
$2[]CFLAGS="$app_cv_gld_reader_CFLAGS";
else
$2[]CFLAGS="$$2[]CFLAGS $app_cv_gld_reader_CFLAGS";
fi
if test "x$$2[]CXXFLAGS" = "x"; then
$2[]CXXFLAGS="$app_cv_gld_reader_CXXFLAGS";
else
$2[]CXXFLAGS="$$2[]CXXFLAGS $app_cv_gld_reader_CXXFLAGS";
fi
if test "x$$2[]LIBS" = "x"; then
$2[]LIBS="$app_cv_gld_reader_LIBS";
else
$2[]LIBS="$$2[]LIBS $app_cv_gld_reader_LIBS";
fi
fi
# export to automake.am-s
AM_CONDITIONAL([ENABLE_LIBGLADE], [test "$app_cv_gld_reader" = "libglade"])
AM_CONDITIONAL([ENABLE_GTKBUILDER], [test "$app_cv_gld_reader" = "gtkbuilder"])
# export to config.h
if test "$app_cv_gld_reader" = "libglade"; then
AC_DEFINE(ENABLE_LIBGLADE, 1, [Use libglade(mm) instead of GtkBuilder])
fi
if test "$app_cv_gld_reader" = "gtkbuilder"; then
AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm])
fi
# If we move this to the start, some messages get in between, so we have to have it here.
AC_MSG_CHECKING([for glade file support:])
AC_MSG_RESULT([$app_cv_gld_reader])
])
+29 -52
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -30,8 +30,8 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# ---- Common compiler options (warnings, etc...)
AC_ARG_ENABLE(common-options, AS_HELP_STRING([--enable-common-options=<compiler>|auto|none],
[enable useful compilation options (warnings, mostly). Accepted values are auto, none, gnu, clang, intel, sun. (Default: auto)]),
[app_cv_compiler_common_options=${enableval}], [app_cv_compiler_common_options=none])
[enable useful compilation options (warnings, mostly). Accepted values are auto, none, gnu, intel, sun. (Default: auto)]),
[app_cv_compiler_common_options=${enableval}], [app_cv_compiler_common_options=auto])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_common_options" = "xyes" || test "x$app_cv_compiler_common_options" = "xauto"; then
@@ -47,26 +47,22 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
if test "x$app_cv_compiler_common_options" != "xnone"; then
# gcc (or clang) / gnu environment, mingw, cygwin.
if test "x$app_cv_compiler_common_options" = "xgnu" || test "x$app_cv_compiler_common_options" = "xclang"; then
# gcc / gnu environment, mingw, cygwin.
if test "x$app_cv_compiler_common_options" = "xgnu"; then
if test "x$app_cv_target_os_env" = "xmingw32" || test "x$app_cv_target_os_env" = "xmingw64" \
|| test "x$app_cv_target_os_env" = "xcygwin"; then
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; then
# mingw gcc options:
# -mno-cygwin - generate non-cygwin executables in cygwin's mingw.
# -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin. Default for mingw.
# -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mms-bitfields"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mms-bitfields"
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mms-bitfields"
fi
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual -Wconversion \
-Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
# Note: Disabled -Wconversion, it was causing lots of silly warnings under x86-64.
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual \
-Wctor-dtor-privacy -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
-Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings";
if test "x$app_cv_compiler_common_options" = "xclang"; then
# these are really annoying
app_cv_compiler_tmp_var="$app_cv_compiler_tmp_var -Wno-sign-conversion -Wno-format-security"
fi
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags $app_cv_compiler_tmp_var";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags $app_cv_compiler_tmp_var";
@@ -82,16 +78,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# suncc
elif test "x$app_cv_compiler_common_options" = "xsun"; then
# Enable useful language extensions (e.g. __func__ in C++). Details at
# http://docs.sun.com/app/docs/doc/820-7599/bkapy?a=view
# Additional options: "-staticlib=Crun -staticlib=Cstd" - link statically to
# C++ runtime and standard libraries.
# http://docs.sun.com/app/docs/doc/820-7599/bkaws?a=view
# STLport seems to be more standards-compliant, but incompatible
# with default Cstd. Also, it's not guaranteed to keep compatibility
# between versions. Select it with "-library=stlport4".
# http://docs.sun.com/app/docs/doc/820-7599/bkakg?a=view
# http://developers.sun.com/solaris/articles/cmp_stlport_libCstd.html
# http://docs.sun.com/app/docs/doc/819-5267/6n7c46drp?a=view
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags \
+w -errtags -erroff=notemsource,notused -features=extensions";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \
@@ -106,7 +93,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# ---- Compiler options for debug builds
AC_ARG_ENABLE(debug-options, AS_HELP_STRING([--enable-debug-options=<compiler>|auto|none],
[enable debug build options. Accepted values are auto, none, gnu, clang, intel, sun. (Default: none)]),
[enable debug build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_debug_options=${enableval}], [app_cv_compiler_debug_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
@@ -128,7 +115,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
AC_DEFINE([DEBUG_BUILD], [1], [Defined for debug builds])
# gcc, mingw
if test "x$app_cv_compiler_debug_options" = "xgnu" || test "x$app_cv_compiler_debug_options" = "xclang"; then
if test "x$app_cv_compiler_debug_options" = "xgnu"; then
# We could put libstdc++ debug options here, but it generates binary-incompatible
# C++ code, which leads to runtime errors with e.g. libsigc++.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0";
@@ -158,7 +145,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# ---- Compiler options for optimized builds
AC_ARG_ENABLE(optimize-options, AS_HELP_STRING([--enable-optimize-options=<compiler>|auto|none],
[enable optimized build options. Accepted values are auto, none, gnu, clang, intel, sun. (Default: none)]),
[enable optimized build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_optimize_options=${enableval}], [app_cv_compiler_optimize_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
@@ -176,30 +163,17 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
if test "x$app_cv_compiler_optimize_options" != "xnone"; then
# gcc, mingw
if test "x$app_cv_compiler_optimize_options" = "xgnu" || test "x$app_cv_compiler_optimize_options" = "xclang"; then
if test "x$app_cv_compiler_optimize_options" = "xgnu"; then
# -mtune=generic is since gcc 4.0 iirc
if test "x$app_cv_target_os_env" = "xmingw32"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -march=i686";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -march=i686";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -march=i686";
else # mingw64, cygwin (they have new gcc), others.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s";
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -march=i586";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -march=i586";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -march=i586";
else
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -mtune=generic";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -mtune=generic";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -mtune=generic";
fi
# -mtune=generic is only for x86*
case "$target" in
i686-*)
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mtune=generic";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mtune=generic";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mtune=generic";
;;
x86_64-*)
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mtune=generic";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mtune=generic";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mtune=generic";
;;
esac
# intel / gnu
elif test "x$app_cv_compiler_optimize_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
@@ -244,7 +218,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# -mconsole - opposite of -mwindows, default.
AC_ARG_ENABLE(windows-console, AS_HELP_STRING([--enable-windows-console=yes|no|auto],
[enable windows console (MinGW and Cygwin only). Accepted values are yes, no, auto. ]
[enable windows console (MinGW only). Accepted values are yes, no, auto. ]
[Auto means disabled for optimized builds, enabled for all others. (Default: auto)]),
[app_cv_compiler_windows_console=${enableval}], [app_cv_compiler_windows_console=auto])
@@ -257,12 +231,15 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
fi
fi
if test "x$app_cv_target_os_env" = "xmingw32" || test "x$app_cv_target_os_env" = "xmingw64" \
|| test "x$app_cv_target_os_env" = "xcygwin"; then
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; then
if test "x$app_cv_compiler_windows_console" = "xno"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mwindows";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mwindows";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mwindows";
else
# specify -mconsole even if it's the default - it should override the user-supplied setting
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mconsole";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mconsole";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mconsole";
fi
AC_MSG_NOTICE([Enable windows console: $app_cv_compiler_windows_console])
+6 -11
View File
@@ -1,14 +1,13 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports overloading of functions
# with function pointer argument differing only in extern "C" specification,
# that is, extern "C" function pointer and C++ function pointer. GCC and
# Check whether C++ compiler supports overloading between
# extern "C" function pointer and C++ function pointer. GCC and
# Intel treat them equally, so no overloads are needed (or accepted)
# there.
@@ -22,8 +21,7 @@
# for template parameters (I don't remember the exact details).
# If such overloading support is found,
# HAVE_CXX_EXTERN_C_OVERLOAD is defined.
# If such support is found, HAVE_CXX_EXTERN_C_OVERLOAD is defined.
AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [
@@ -39,11 +37,8 @@ AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [
AC_LANG_POP([])
])
if test "x$ac_cv_cxx_extern_c_overload" = "xyes"; then
AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, 1,
[Defined to 1 if the C++ complier accepts extern C function pointer overload, 0 otherwise])
else
AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, 0,
[Defined to 1 if the C++ complier accepts extern C function pointer overload, 0 otherwise])
AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, ,
[Define if the C++ complier accepts extern C function pointer overload])
fi
])
+6 -12
View File
@@ -1,14 +1,14 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports C99 __func__.
AC_DEFUN([APP_CXX___func__], [
AC_DEFUN([APP_CXX___FUNC__], [
AC_CACHE_CHECK(whether $CXX recognizes __func__, app_cv_cxx___func__, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
@@ -17,11 +17,8 @@ AC_DEFUN([APP_CXX___func__], [
AC_LANG_POP([])
])
if test "x$app_cv_cxx___func__" = "xyes"; then
AC_DEFINE(HAVE_CXX___func__, 1,
[Defined to 1 if the C++ complier supports __func__, 0 otherwise])
else
AC_DEFINE(HAVE_CXX___func__, 0,
[Defined to 1 if the C++ complier supports __func__, 0 otherwise])
AC_DEFINE(HAVE_CXX___func__, ,
[Define if the C++ complier supports __func__])
fi
])
@@ -38,11 +35,8 @@ AC_DEFUN([APP_CXX___FUNCTION__], [
AC_LANG_POP([])
])
if test "x$app_cv_cxx___FUNCTION__" = "xyes"; then
AC_DEFINE(HAVE_CXX___FUNCTION__, 1,
[Defined to 1 if the C++ complier supports __FUNCTION__, 0 otherwise])
else
AC_DEFINE(HAVE_CXX___FUNCTION__, 0,
[Defined to 1 if the C++ complier supports __FUNCTION__, 0 otherwise])
AC_DEFINE(HAVE_CXX___FUNCTION__, ,
[Define if the C++ complier supports __FUNCTION__])
fi
])
+13 -65
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -59,26 +59,12 @@ AC_DEFUN([APP_DETECT_OS_KERNEL], [
app_cv_[]$1[]_os_kernel_macro="LINUX";
;;
i*86-*mingw* | i*86-*cygwin*)
*mingw* | *cygwin*)
# mingw defines _WIN32. msvc defines _WIN32 (built-in), and WIN32 via windows.h.
# Other mingw defines include: __WINNT, __WINNT__, __WIN32__, __i386, _X86_,
# i386, __i386__, WIN32, __MINGW32__, WINNT (all equal to 1).
app_cv_[]$1[]_os_kernel="windows32";
app_cv_[]$1[]_os_kernel_macro="WINDOWS32";
;;
x86_64-*-mingw* | x86_64-*-cygwin*)
# mingw64 defines the same stuff as 32-bit one, plus _WIN64, __MINGW64__, etc... .
# Keep in mind that if you're generating a 32-bit application, the kernel will
# be windows32 even if you run it on 64-bit Windows.
app_cv_[]$1[]_os_kernel="windows64";
app_cv_[]$1[]_os_kernel_macro="WINDOWS64";
;;
*interix*)
# Interix is a kernel on top of windows, so we treat it separately.
app_cv_[]$1[]_os_kernel="interix";
app_cv_[]$1[]_os_kernel_macro="INTERIX";
app_cv_[]$1[]_os_kernel="windows";
app_cv_[]$1[]_os_kernel_macro="WINDOWS";
;;
# This includes debian gnu/kfreebsd. Dragonfly is checked separately.
@@ -138,14 +124,8 @@ AC_DEFUN([APP_DETECT_OS_KERNEL], [
if test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"; then
AC_DEFINE($2[]LINUX, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32"; then
AC_DEFINE($2[]WINDOWS32, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64"; then
AC_DEFINE($2[]WINDOWS64, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"; then
AC_DEFINE($2[]INTERIX, 1, [$1 OS kernel])
if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows"; then
AC_DEFINE($2[]WINDOWS, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS kernel])
@@ -174,9 +154,7 @@ AC_DEFUN([APP_DETECT_OS_KERNEL], [
# Export to Makefile.am's.
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_LINUX, [test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS32, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS64, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_INTERIX, [test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_FREEBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DRAGONFLY, [test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_OPENBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"])
@@ -186,19 +164,6 @@ AC_DEFUN([APP_DETECT_OS_KERNEL], [
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_QNX, [test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_UNKNOWN, [test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"])
# composite ones, for easy checking
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_FAMILY_WINDOWS, \
[test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32" || test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64" \
|| test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"])
if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32" || test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64" \
|| test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"; then
AC_DEFINE($2[]FAMILY_WINDOWS, 1, [$1 OS kernel family])
fi
AC_MSG_RESULT([${app_cv_[]$1[]_os_kernel}])
])
@@ -251,17 +216,9 @@ AC_DEFUN([APP_DETECT_OS_ENV], [
app_cv_[]$1[]_os_env="cygwin";
app_cv_[]$1[]_os_env_macro="CYGWIN";
;;
i*86-*mingw*)
app_cv_[]$1[]_os_env="mingw32";
app_cv_[]$1[]_os_env_macro="MINGW32";
;;
x86_64-*-mingw*)
app_cv_[]$1[]_os_env="mingw64";
app_cv_[]$1[]_os_env_macro="MINGW64";
;;
*interix*)
app_cv_[]$1[]_os_env="interix";
app_cv_[]$1[]_os_env_macro="INTERIX";
*mingw*)
app_cv_[]$1[]_os_env="mingw";
app_cv_[]$1[]_os_env_macro="MINGW";
;;
# This includes gnu/linux, debian gnu/kfreebsd, debian gnu/netbsd...
*-gnu | gnu*)
@@ -314,14 +271,8 @@ AC_DEFUN([APP_DETECT_OS_ENV], [
if test "x${app_cv_[]$1[]_os_env}" = "xcygwin"; then
AC_DEFINE($2[]CYGWIN, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xmingw32"; then
AC_DEFINE($2[]MINGW32, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xmingw64"; then
AC_DEFINE($2[]MINGW64, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xinterix"; then
AC_DEFINE($2[]INTERIX, 1, [$1 OS userspace environment])
if test "x${app_cv_[]$1[]_os_env}" = "xmingw"; then
AC_DEFINE($2[]MINGW, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xgnu"; then
AC_DEFINE($2[]GNU, 1, [$1 OS userspace environment])
@@ -353,9 +304,7 @@ AC_DEFUN([APP_DETECT_OS_ENV], [
# Export to Makefile.am's.
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_CYGWIN, [test "x${app_cv_[]$1[]_os_env}" = "xcygwin"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW32, [test "x${app_cv_[]$1[]_os_env}" = "xmingw32"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW64, [test "x${app_cv_[]$1[]_os_env}" = "xmingw64"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_INTERIX, [test "x${app_cv_[]$1[]_os_env}" = "xinterix"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW, [test "x${app_cv_[]$1[]_os_env}" = "xmingw"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_GNU, [test "x${app_cv_[]$1[]_os_env}" = "xgnu"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_FREEBSD, [test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DRAGONFLY, [test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"])
@@ -366,7 +315,6 @@ AC_DEFUN([APP_DETECT_OS_ENV], [
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_QNX, [test "x${app_cv_[]$1[]_os_env}" = "xqnx"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_UNKNOWN, [test "x${app_cv_[]$1[]_os_env}" = "xunknown"])
AC_MSG_RESULT([${app_cv_[]$1[]_os_env}])
])
+27 -64
View File
@@ -1,14 +1,14 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_GET_MT_FLAGS(flags_prefix[, action-if-not-found])
# This macro detects compiler flags which are needed to compile a multi-threaded
# (preferably pthreads-based) applications on target OS. flags_prefixCFLAGS,
# (possibly pthreads-based) applications on target OS. flags_prefixCFLAGS,
# flags_prefixCXXFLAGS and flags_prefixLIBS will be initialized (or appended to)
# with the correct flags.
@@ -47,7 +47,7 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
fi
# Compilers use at least 3 defines to enable thread-safe support:
# Compilers use at least 3 defines to indicate thread-safe support to standard libs:
# _THREAD_SAFE, _MT, _REENTRANT.
# We use the ones that are required for detected platform.
@@ -55,34 +55,31 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
linux)
# glibc manual says that defining either _REENTRANT or _THREAD_SAFE
# enables some thread-safe implementations of glibc functions.
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang" \
|| test "$ax_cv_cxx_compiler_vendor" = "intel" || test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel" \
|| test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# suncc's -mt expands to:
# "-D_REENTRANT -lpthread" on Linux, uses pthreads (pthread.h);
# "-D_REENTRANT -lthread" on Solaris, uses Solaris threads (thread.h).
# To use pthreads on Solaris, use "-mt -lpthread".
# http://docs.sun.com/app/docs/doc/820-7598/bjapp?a=view
# suncc needs -lpthread on posix but not solaris.
# -mt defines _REENTRANT too. Don't use -lthread.
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt"
app_cv_target_thread_libs="-mt -lpthread"
app_cv_target_thread_found="yes";
fi
;;
windows*)
# NOTE: Not sure about cygwin or clang.
windows)
# NOTE: Not sure about cygwin.
# NOTE: This only enables multithreaded code generation. For linking
# with pthreads-win32 you need additional flags.
# gcc man page: -mthreads: Support thread-safe exception handling on Mingw32.
# -mthreads defines -D_MT, and links with -lmingwthrd.
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_libs="-mthreads"
@@ -90,21 +87,11 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
fi
;;
interix)
# Interix kernel is implemented on top of windows kernel.
# Interix uses -D_REENTRANT for pthreads, automatically enabling -lpthread.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-D_REENTRANT"
app_cv_target_thread_cxxflags="-D_REENTRANT"
app_cv_target_thread_found="yes"
fi
;;
freebsd | dragonfly)
freebsd | dragonfly | netbsd | openbsd)
# NOTE: Not sure about debian gnu/kfreebsd and gnu/netbsd.
# Freebsd had -kthread, but it has been discontinued (afaik).
# For freebsd -pthread replaces libc with libc_r.
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
# For freebsd -pthread already replaces libc with libc_r.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
@@ -112,61 +99,37 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
fi
;;
openbsd)
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
app_cv_target_thread_cflags="-pthread -D_REENTRANT"
app_cv_target_thread_cxxflags="-pthread -D_REENTRANT"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
fi
;;
netbsd)
# See sys/featuretest.h
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
app_cv_target_thread_cflags="-pthread -D_REENTRANT"
app_cv_target_thread_cxxflags="-pthread -D_REENTRANT"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
fi
;;
solaris)
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
# gnu documentation mentions -threads (and its aliases, -pthreads and -pthread).
# _REENTRANT is needed to enable some thread-safe equivalents of standard
# functions.
# http://docs.sun.com/app/docs/doc/805-8005-04/6j7hcvqs4?l=ko&a=view
app_cv_target_thread_cflags="-pthreads -D_MT -D_THREAD_SAFE -D_REENTRANT"
app_cv_target_thread_cxxflags="-pthreads -D_MT -D_THREAD_SAFE -D_REENTRANT"
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# gnu documentation mentions -threads (and its aliases, -pthreads and -pthread)
app_cv_target_thread_cflags="-pthreads -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthreads -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthreads"
app_cv_target_thread_found="yes"
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# suncc's -mt expands to:
# "-D_REENTRANT -lpthread" on Linux, uses pthreads (pthread.h);
# "-D_REENTRANT -lthread" on Solaris, uses Solaris threads (thread.h).
# To use pthreads on Solaris, use "-mt -lpthread".
# http://docs.sun.com/app/docs/doc/820-7598/bjapp?a=view
# -mt defines _REENTRANT too. Don't use -lthread.
# http://docs.sun.com/app/docs/doc/816-5175/pthreads-5?a=view
# http://docs.sun.com/app/docs/doc/816-5137/compile-74765?a=view
# We use POSIX threads, so append -lpthread.
app_cv_target_thread_cflags="-mt -lpthread"
app_cv_target_thread_cxxflags="-mt -lpthread"
app_cv_target_thread_libs="-mt -lpthread"
# There are two threading libraries, selectable via different switches.
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt"
app_cv_target_thread_found="yes"
fi
;;
darwin)
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# Darwin produces mt code by default, and -pthread is an error.
# gcc/darwin defines __APPLE__. __UNIX__ is not defined.
# -D_THREAD_SAFE
app_cv_target_thread_found="yes"
fi
;;
qnx)
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# afaik, gcc/qnx produces mt code by default (at least on newer qnx releases),
# so no flags are needed.
app_cv_target_thread_found="yes"
+3 -39
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -13,9 +13,6 @@
# Basically, it's the same as AC_USE_SYSTEM_EXTENSIONS,
# but uses compiler flag approach instead of config.h.
# The extensions used bring the system closer to the GNU,
# X/Open (a superset of POSIX) and POSIX systems.
# You must call AX_COMPILER_VENDOR
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
@@ -69,43 +66,10 @@ AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [
solaris)
# The problem with solaris is that it has too many incompatible
# feature test macros, and there is no enable-all macro. Autoconf
# assumes that __EXTENSIONS__ is the one, but it should be defined
# _in addition_ to feature test macros. Note that on its own, __EXTENSIONS__
# still enables some useful stuff like _LARGEFILE64_SOURCE.
# mistakenly assumes that __EXTENSIONS__ is the one, but
# it should be defined _in addition_ to feature test macros.
# To support some posix macros, additional link objects are also required.
# Some *_SOURCE feature test macros may be incompatible with C++.
# http://docs.sun.com/app/docs/doc/816-5175/standards-5?a=view
app_cv_target_extension_cflags="-D__EXTENSIONS__"
app_cv_target_extension_cxxflags="-D__EXTENSIONS__"
;;
netbsd)
# There is no enable-all macro.
# (_XOPEN_SOURCE >= 500 || _NETBSD_SOURCE) is recommended.
# The default is _NETBSD_SOURCE, _XOPEN_SOURCE overrides it.
# The manual says that rename() will behave posix-like if _POSIX_C_SOURCE
# is defined. However, stdio.h uses (_XOPEN_SOURCE <= 2) condition,
# which is really bogus.
# See sys/featuretest.h
;;
mingw*)
# This enables standards-compliant stdio behaviour (regarding printf and
# friends), as opposed to msvc-compatible one. This is usually enabled
# by default if one of the usual macros are encountered (_XOPEN_SOURCE,
# _GNU_SOURCE, etc...).
# Also, we make the win2k API available (includes console functions, etc...).
# Other things to consider: _NO_OLDNAMES (disable "deprecated"
# non-underscored names like chdir (use _chdir), etc...).
# See _mingw.h for details.
app_cv_target_extension_cflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0600" # Since Vista
app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0600"
;;
interix)
# As per AC_USE_SYSTEM_EXTENSIONS.
app_cv_target_extension_cflags="-D_ALL_SOURCE"
app_cv_target_extension_cxxflags="-D_ALL_SOURCE"
;;
esac
+1 -1
View File
@@ -53,7 +53,7 @@ AC_DEFUN([AX_COMPILER_VENDOR],
AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor,
[ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown
# note: don't check for gcc first since some other compilers define __GNUC__
for ventest in clang:__clang__ intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do
for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do
vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
#if !($vencpp)
-982
View File
@@ -1,982 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 7
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AX_REQUIRE_DEFINED([AC_MSG_WARN])
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
ax_cv_cxx_compile_cxx$1,
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[ax_cv_cxx_compile_cxx$1=yes],
[ax_cv_cxx_compile_cxx$1=no])])
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
ac_success=yes
fi
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for alternative in ${ax_cxx_compile_alternatives}; do
switch="-std=gnu++${alternative}"
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
if test x$ac_success = xyes; then
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])
dnl Tests for new features in C++17
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
// If the compiler admits that it is not ready for C++17, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus <= 201402L
#error "This is not a C++17 compiler"
#else
#if defined(__clang__)
#define REALLY_CLANG
#else
#if defined(__GNUC__)
#define REALLY_GCC
#endif
#endif
#include <initializer_list>
#include <utility>
#include <type_traits>
namespace cxx17
{
#if !defined(REALLY_CLANG)
namespace test_constexpr_lambdas
{
// TODO: test it with clang++ from git
constexpr int foo = [](){return 42;}();
}
#endif // !defined(REALLY_CLANG)
namespace test::nested_namespace::definitions
{
}
namespace test_fold_expression
{
template<typename... Args>
int multiply(Args... args)
{
return (args * ... * 1);
}
template<typename... Args>
bool all(Args... args)
{
return (args && ...);
}
}
namespace test_extended_static_assert
{
static_assert (true);
}
namespace test_auto_brace_init_list
{
auto foo = {5};
auto bar {5};
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
static_assert(std::is_same<int, decltype(bar)>::value);
}
namespace test_typename_in_template_template_parameter
{
template<template<typename> typename X> struct D;
}
namespace test_fallthrough_nodiscard_maybe_unused_attributes
{
int f1()
{
return 42;
}
[[nodiscard]] int f2()
{
[[maybe_unused]] auto unused = f1();
switch (f1())
{
case 17:
f1();
[[fallthrough]];
case 42:
f1();
}
return f1();
}
}
namespace test_extended_aggregate_initialization
{
struct base1
{
int b1, b2 = 42;
};
struct base2
{
base2() {
b3 = 42;
}
int b3;
};
struct derived : base1, base2
{
int d;
};
derived d1 {{1, 2}, {}, 4}; // full initialization
derived d2 {{}, {}, 4}; // value-initialized bases
}
namespace test_general_range_based_for_loop
{
struct iter
{
int i;
int& operator* ()
{
return i;
}
const int& operator* () const
{
return i;
}
iter& operator++()
{
++i;
return *this;
}
};
struct sentinel
{
int i;
};
bool operator== (const iter& i, const sentinel& s)
{
return i.i == s.i;
}
bool operator!= (const iter& i, const sentinel& s)
{
return !(i == s);
}
struct range
{
iter begin() const
{
return {0};
}
sentinel end() const
{
return {5};
}
};
void f()
{
range r {};
for (auto i : r)
{
[[maybe_unused]] auto v = i;
}
}
}
namespace test_lambda_capture_asterisk_this_by_value
{
struct t
{
int i;
int foo()
{
return [*this]()
{
return i;
}();
}
};
}
namespace test_enum_class_construction
{
enum class byte : unsigned char
{};
byte foo {42};
}
namespace test_constexpr_if
{
template <bool cond>
int f ()
{
if constexpr(cond)
{
return 13;
}
else
{
return 42;
}
}
}
namespace test_selection_statement_with_initializer
{
int f()
{
return 13;
}
int f2()
{
if (auto i = f(); i > 0)
{
return 3;
}
switch (auto i = f(); i + 4)
{
case 17:
return 2;
default:
return 1;
}
}
}
#if !defined(REALLY_CLANG)
namespace test_template_argument_deduction_for_class_templates
{
// TODO: test it with clang++ from git
template <typename T1, typename T2>
struct pair
{
pair (T1 p1, T2 p2)
: m1 {p1},
m2 {p2}
{}
T1 m1;
T2 m2;
};
void f()
{
[[maybe_unused]] auto p = pair{13, 42u};
}
}
#endif // !defined(REALLY_CLANG)
namespace test_non_type_auto_template_parameters
{
template <auto n>
struct B
{};
B<5> b1;
B<'a'> b2;
}
#if !defined(REALLY_CLANG)
namespace test_structured_bindings
{
// TODO: test it with clang++ from git
int arr[2] = { 1, 2 };
std::pair<int, int> pr = { 1, 2 };
auto f1() -> int(&)[2]
{
return arr;
}
auto f2() -> std::pair<int, int>&
{
return pr;
}
struct S
{
int x1 : 2;
volatile double y1;
};
S f3()
{
return {};
}
auto [ x1, y1 ] = f1();
auto& [ xr1, yr1 ] = f1();
auto [ x2, y2 ] = f2();
auto& [ xr2, yr2 ] = f2();
const auto [ x3, y3 ] = f3();
}
#endif // !defined(REALLY_CLANG)
#if !defined(REALLY_CLANG)
namespace test_exception_spec_type_system
{
// TODO: test it with clang++ from git
struct Good {};
struct Bad {};
void g1() noexcept;
void g2();
template<typename T>
Bad
f(T*, T*);
template<typename T1, typename T2>
Good
f(T1*, T2*);
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
}
#endif // !defined(REALLY_CLANG)
namespace test_inline_variables
{
template<class T> void f(T)
{}
template<class T> inline T g(T)
{
return T{};
}
template<> inline void f<>(int)
{}
template<> int g<>(int)
{
return 5;
}
}
} // namespace cxx17
#endif // __cplusplus <= 201402L
]])
+21 -32
View File
@@ -28,40 +28,29 @@
AC_DEFUN([AX_CXX_GCC_ABI_DEMANGLE],
[AC_CACHE_CHECK(whether the compiler supports GCC C++ ABI name demangling,
ac_cv_cxx_gcc_abi_demangle,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <typeinfo>
#include <cxxabi.h>
#include <string>
#include <stdlib.h> /* not cstdlib */
ac_cv_cxx_gcc_abi_demangle,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <typeinfo>
#include <cxxabi.h>
#include <string>
template<typename TYPE>
class A {};
]], [[
A<int> instance;
int status = 0;
char* c_name = 0;
template<typename TYPE>
class A {};
]], [[A<int> instance;
int status = 0;
char* c_name = 0;
c_name = abi::__cxa_demangle(typeid(instance).name(), 0, 0, &status);
c_name = abi::__cxa_demangle(typeid(instance).name(), 0, 0, &status);
std::string name(c_name);
free(c_name); /* not std::free() */
std::string name(c_name);
free(c_name);
return name == "A<int>";
]])],
[ac_cv_cxx_gcc_abi_demangle=yes], [ac_cv_cxx_gcc_abi_demangle=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_gcc_abi_demangle" = yes; then
AC_DEFINE(HAVE_GCC_ABI_DEMANGLE, 1,
[defined to 1 if the compiler supports GCC C++ ABI name demangling, 0 otherwise])
else
AC_DEFINE(HAVE_GCC_ABI_DEMANGLE, 0,
[defined to 1 if the compiler supports GCC C++ ABI name demangling, 0 otherwise])
fi
return name == "A<int>";
]])],[ac_cv_cxx_gcc_abi_demangle=yes],[ac_cv_cxx_gcc_abi_demangle=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_gcc_abi_demangle" = yes; then
AC_DEFINE(HAVE_GCC_ABI_DEMANGLE,1,
[define if the compiler supports GCC C++ ABI name demangling])
fi
])
+1 -1
View File
@@ -16,7 +16,7 @@
# autoconf
autoreconf --verbose --install -W all --force
autoreconf --verbose --install -W all
rm -f config.cache
Executable
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
stamp="../gsmartcontrol-`date +%F`"
echo $stamp
mkdir $stamp
cp -dp * $stamp/
rm -f $stamp/*pcs # kdevelop persistent store files
cp -a autoconf.m4/ $stamp/
cp -a data/ $stamp/
cp -a src/ $stamp/
cp -a templates/ $stamp/
# cp -a test-data/ $stamp/
tar cvf $stamp.tar $stamp
bzip2 -9 $stamp.tar
-87
View File
@@ -1,87 +0,0 @@
#!/bin/bash
# Configure a build directory according to developer flags
type="$1";
configure_script="$2";
if [ "$type" == "" ]; then
echo "Usage: $0 <type> [<configure_script>]"
echo "where <type> is one of:"
echo "none debug optimized gcc-debug clang-debug icc-debug sun win32-cross win32-cross-debug \
win64-cross win64-cross-debug win32-msys win32-msys-debug win64-msys win64-msys-debug";
echo "<configure_script> is ../configure by default"
exit 1;
fi
if [ "$configure_script" == "" ]; then
configure_script="../configure";
fi
flags="";
case $type in
none)
flags="--enable-tests" ;;
debug)
flags="--enable-debug-options --enable-tests" ;;
optimized)
flags="--enable-optimize-options --enable-tests" ;;
gcc-debug)
flags="CC=gcc CXX=g++ --enable-common-options=gnu --enable-debug-options --enable-tests" ;;
clang-debug)
flags="CC=clang CXX=clang++ --enable-common-options=clang --enable-debug-options --enable-tests" ;;
icc-debug)
flags="CC=icc64 CXX=icpc64 --enable-common-options=intel --enable-debug-options --enable-tests" ;;
sun)
flags="CC=suncc CXX=sunCC --enable-common-options=sun CFLAGS=\"-errwarn=%all -I/opt/sun/target64/include\" \
CXXFLAGS=\"-I/opt/sun/target64/include\" PATH=\"/opt/sun/sunstudio/bin:$PATH\" \
LDFLAGS=\"-R/opt/sun/target64/lib64 -L/opt/sun/target64/lib64\" \
PKG_CONFIG_PATH=\"/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig\" \
PKG_CONFIG_LIBDIR=\"\" --enable-tests" ;;
win32-cross)
flags="PATH=\"/usr/i686-w64-mingw32/bin:/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH\" \
--with-nsis=/usr/bin/makensis --with-windows-sysroot=/usr/i686-w64-mingw32/sys-root/mingw \
--enable-optimize-options --enable-tests --host=i686-w64-mingw32" ;;
win32-cross-debug)
flags="PATH=\"/usr/i686-w64-mingw32/bin:/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH\" \
--with-windows-sysroot=/usr/i686-w64-mingw32/sys-root/mingw \
--enable-debug-options --enable-tests --host=i686-w64-mingw32" ;;
win64-cross)
flags="PATH=\"/usr/x86_64-w64-mingw32/bin:/usr/x86_64-w64-mingw32/sys-root/mingw/bin:$PATH\" \
--with-nsis=/usr/bin/makensis --with-windows-sysroot=/usr/x86_64-w64-mingw32/sys-root/mingw \
--enable-optimize-options --enable-tests --host=x86_64-w64-mingw32" ;;
win64-cross-debug)
flags="PATH=\"/usr/x86_64-w64-mingw32/bin:/usr/x86_64-w64-mingw32/sys-root/mingw/bin:$PATH\" \
--with-windows-sysroot=/usr/x86_64-w64-mingw32/sys-root/mingw \
--enable-debug-options --enable-tests --host=x86_64-w64-mingw32" ;;
win32-msys)
flags="PATH=\"/mingw32/i686-w64-mingw32:/mingw32/bin:$PATH\" \
--with-nsis=/mingw32/bin/makensis.exe --with-windows-sysroot=/mingw32 \
--enable-optimize-options --enable-tests --host=i686-w64-mingw32" ;;
win32-msys-debug)
flags="PATH=\"/mingw32/i686-w64-mingw32:/mingw32/bin:$PATH\" \
--with-windows-sysroot=/mingw32 \
--enable-debug-options --enable-tests --host=i686-w64-mingw32" ;;
win64-msys)
flags="PATH=\"/mingw64/x86_64-w64-mingw32:/mingw64/bin:$PATH\" \
--with-nsis=/mingw64/bin/makensis.exe --with-windows-sysroot=/mingw64 \
--enable-optimize-options --enable-tests --host=x86_64-w64-mingw32" ;;
win64-msys-debug)
flags="PATH=\"/mingw64/x86_64-w64-mingw32:/mingw64/bin:$PATH\" \
--with-windows-sysroot=/mingw64 \
--enable-debug-options --enable-tests --host=x86_64-w64-mingw32" ;;
esac
echo "Running:"
echo $configure_script $flags;
$configure_script $flags
exit $?
+127 -106
View File
@@ -1,5 +1,5 @@
AC_INIT([GSmartControl],[1.1.4],[],[gsmartcontrol])
AC_INIT([GSmartControl], 0.8.4, [], [gsmartcontrol])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS([config.h])
@@ -22,7 +22,6 @@ AC_PROG_CXX
#AC_PROG_CXXCPP
#AM_PROG_LIBTOOL
AM_PROG_AR
# C++ support in checks
#AC_LANG([C])
@@ -64,18 +63,8 @@ APP_DETECT_OS_KERNEL([build], [CONFIG_HOST_KERNEL_])
# which is important for some compatibility flags (e.g. __func__ visibility, etc...).
APP_COMPILER_OPTIONS([])
# Enable C++11 support. This is implied by gtkmm3, but CentOS7 fails to add it
# to gtkmm flags, so we have to enable it manually. Make it optional for Debian 7 (gcc 4.7).
AX_CXX_COMPILE_STDCXX([11], [], [optional])
# Thread support for known configurations.
# Sun compiler (at least on linux) has some runtime issues with stringstream
# unless we use the thread flags. Also, Linux-based gdb fails to debug
# a gcc-compiled binary for some reason.
# Do this for all systems except mingw.
if test "$app_cv_target_os_env" != "mingw32" && test "$app_cv_target_os_env" != "mingw64"; then
APP_GET_MT_FLAGS([])
fi
APP_GET_MT_FLAGS([])
# Enable _GNU_SOURCE, etc...
APP_USE_SYSTEM_EXTENSIONS([])
@@ -119,7 +108,7 @@ AX_CXX_GCC_ABI_DEMANGLE
AC_CXX_VERBOSE_TERMINATE_HANDLER
# detect __func__ support (defines HAVE_CXX___func__)
APP_CXX___func__
APP_CXX___FUNC__
# detect __FUNCTION__ support (defines HAVE_CXX___FUNCTION__)
APP_CXX___FUNCTION__
@@ -132,12 +121,14 @@ APP_CXX_EXTERN_C_OVERLOAD
# This may be needed for pch support
AC_CHECK_HEADERS([stdc++.h])
# pcrecpp needs this (break on first function found)
AC_CHECK_FUNCS([strtoll _strtoi64], [break])
# Non-C99 systems don't have strtof and strtold. some have strtodf for strtof,
# but let's ignore it. Note: They may be supported by system libc,
# but not enabled by default (e.g. by glibc feature macros). In that case,
# defining them still leads to undefined symbols at link time, so we don't do it.
# Header check is the best way to determine availability.
# This defines HAVE_DECL_<symbol> to 1 or 0.
# Header check is the best way to determine availability:
AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# link check only:
# AC_CHECK_FUNCS([strtof strtold])
@@ -177,21 +168,16 @@ AC_ARG_ENABLE(abort-if-no-gtkmm, AS_HELP_STRING([--disable-abort-if-no-gtkmm],
AC_MSG_NOTICE([Abort if no gtkmm is found: $app_cv_abort_if_no_gtkmm])
pkg_modules="gtkmm-3.0 >= 3.4.0"
# iconview is available since 2.6.0. gthread is not enabled by default.
pkg_modules="gtkmm-2.4 >= 2.6.0 gthread-2.0 >= 2.0"
# define ENABLE_GLIB and friends if it's found.
gtkmm_found="no"
PKG_CHECK_MODULES([GTKMM], [$pkg_modules],
[gtkmm_found="yes"], [gtkmm_found="no"])
if test "x$gtkmm_found" = "xyes"; then
AC_DEFINE([ENABLE_GLIB], [1], [Defined to 1 if GLib is installed, 0 otherwise])
AC_DEFINE([ENABLE_GLIBMM], [1], [Defined to 1 if glibmm is installed, 0 otherwise])
else
AC_DEFINE([ENABLE_GLIB], [0], [Defined to 1 if GLib is installed, 0 otherwise])
AC_DEFINE([ENABLE_GLIBMM], [0], [Defined to 1 if glibmm is installed, 0 otherwise])
fi
PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [
AC_DEFINE([ENABLE_GLIB], [1], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [1], [Whether GLibmm is installed])
gtkmm_found="yes"], [gtkmm_found="no"])
if test "x$gtkmm_found" = "xno"; then
if test "x$app_cv_abort_if_no_gtkmm" = "xno"; then
@@ -202,38 +188,67 @@ if test "x$gtkmm_found" = "xno"; then
fi
fi
# GTK flags must be prepended, or glib_config.h conflict between
# gtk 2.x and 1.2 may occur.
#CFLAGS="$GTKMM_CFLAGS $CFLAGS"
CXXFLAGS="$GTKMM_CFLAGS $CXXFLAGS"
LIBS="$GTKMM_LIBS $LIBS"
# Detect libglade / gtkbuilder support
APP_CHOOSE_GLD_READER([C++], [LIBGLADE_])
if test "$app_cv_gld_reader" = "libglade"; then
CFLAGS="$CFLAGS $LIBGLADE_CFLAGS"
CXXFLAGS="$CXXFLAGS $LIBGLADE_CXXFLAGS"
LIBS="$LIBS $LIBGLADE_LIBS"
fi
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
PCRECPP_CFLAGS="";
PCRECPP_LIBS="";
PCRE_CFLAGS="";
PCRE_LIBS="";
# try pkg-config first
PKG_CHECK_MODULES(PCRECPP, [libpcrecpp], [pcrecpp_found=yes], [pcrecpp_found=no])
PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no])
# if not found, try pcre-config (solaris had it but not pkg-config).
if test "x$pcrecpp_found" = "xno"; then
if test "x$pcre_found" = "xno"; then
AC_PATH_PROG(pcre_config_binary, [pcre-config])
if test "x$pcre_config_binary" != "x"; then
PCRE_CFLAGS="`pcre-config --cflags`"
PCRE_LIBS="`pcre-config --libs-cpp`"
PCRE_LIBS="`pcre-config --libs`"
else
# AC_MSG_ERROR([Could not find pcrecpp through pkg-config and pcre-config. Make sure the pcre and pcrecpp libraries are installed])
AC_MSG_ERROR([$PCRECPP_PKG_ERRORS])
# AC_MSG_ERROR([Could not find pcre through pkg-config and pcre-config. Make sure the pcre libraries are installed])
AC_MSG_ERROR([$PCRE_PKG_ERRORS])
fi
fi
CFLAGS="$CFLAGS $PCRECPP_CFLAGS"
CXXFLAGS="$CXXFLAGS $PCRECPP_CFLAGS"
LIBS="$LIBS $PCRECPP_LIBS"
CFLAGS="$CFLAGS $PCRE_CFLAGS"
CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS"
LIBS="$LIBS $PCRE_LIBS"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Test stuff here...
# <nothing>
#SDL_VERSION=1.2.0
#AM_PATH_SDL($SDL_VERSION, :,
# AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) )
#CFLAGS="$CFLAGS $SDL_CFLAGS"
#CXXFLAGS="$CXXFLAGS $SDL_CFLAGS "
#LIBS="$LIBS $SDL_LIBS"
# -------------------------------------------------------------------------------------
@@ -246,46 +261,45 @@ LIBS="$LIBS $PCRECPP_LIBS"
AC_ARG_WITH([nsis], [AS_HELP_STRING([--with-nsis=<location>|auto],
[specify makensis.exe location to support making NSIS packages]
[ (default: "C:\Program Files\NSIS\makensis.exe" for windows, ]
["~/.wine/drive_c/Program Files/NSIS/Unicode/makensis.exe" for others)])],
["~/.wine/drive_c/Program Files/NSIS/makensis.exe" for others)])],
[with_nsis=${withval}], [with_nsis=auto])
AC_ARG_ENABLE([nsis-wine], [AS_HELP_STRING([--enable-nsis-wine=yes|no],
[use wine to run makensis.exe when making NSIS packages (default: no)])],
[with_nsis_wine=${enableval}], [with_nsis_wine=no])
AC_ARG_ENABLE([nsis-wine], [AS_HELP_STRING([--enable-nsis-wine=yes|no|auto],
[use wine to run makensis.exe when making NSIS packages (default: auto, which is no for windows, yes for others)])],
[with_nsis_wine=${enableval}], [with_nsis_wine=yes])
AC_ARG_WITH([windows-sysroot], [AS_HELP_STRING([--with-windows-sysroot],
[specify location of Windows system root directory when making Windows packages (default: /mingw32)])],
[with_windows_sysroot=${withval}], [with_windows_sysroot=auto])
AC_ARG_WITH([windows-dlls], [AS_HELP_STRING([--with-windows-dlls],
[specify location of Windows dlls when making Windows packages (default: /target/bin)])],
[with_windows_env=${withval}], [win_windows_dlls=auto])
if test "x$with_nsis" = "xauto" || test "x$with_nsis" = "x"; then
# if building on windows, use native paths
if test "x$app_cv_build_os_kernel" = "xwindows32" || test "x$app_cv_build_os_kernel" = "xwindows64"; then
with_nsis="\"C:\\Program Files\\NSIS\\makensis.exe\"";
if test "x$app_cv_build_os_kernel" = "xwindows"; then
with_nsis="~/\".wine/drive_c/Program Files/NSIS/makensis.exe\"";
else
with_nsis="~/\".wine/drive_c/Program Files/NSIS/Unicode/makensis.exe\"";
with_nsis="\"C:\\Program Files\\NSIS\\makensis.exe\"";
fi
fi
if test "x$with_nsis_wine" = "xauto"; then
if test "x$app_cv_build_os_kernel" = "xwindows"; then
with_nsis_wine="no";
else
with_nsis_wine="yes";
fi
fi
NSIS_EXEC="$with_nsis";
if test "x$with_nsis_wine" = "xyes"; then
if test "x$with_nsis_wine" != "xno"; then
NSIS_EXEC="wine $with_nsis";
fi
if test "x$with_windows_sysroot" = "xauto" || test "x$with_windows_sysroot" = "x"; then
with_windows_sysroot="/mingw32"
fi
WINDOWS_SYSROOT="$with_windows_sysroot";
WINDOWS_SUFFIX="";
if test "x$app_cv_target_os_kernel" = "xwindows32"; then
WINDOWS_SUFFIX="win32"
elif test "x$app_cv_target_os_kernel" = "xwindows64"; then
WINDOWS_SUFFIX="win64"
if test "x$with_windows_dlls" = "xauto" || test "x$with_windows_dlls" = "x"; then
with_windows_dlls="/target/bin"
fi
WINDOWS_DLLS_LOCATION="$with_windows_dlls";
AC_SUBST(NSIS_EXEC)
AC_SUBST(WINDOWS_SYSROOT)
AC_SUBST(WINDOWS_SUFFIX)
AC_SUBST(WINDOWS_DLLS_LOCATION)
# -------------------------------------------------------------------------------------
@@ -293,37 +307,25 @@ AC_SUBST(WINDOWS_SUFFIX)
# -------------------------------------------------------------------------------------
# Win32 security manifest support
WINDOWS_ARCH=""
if test "x$app_cv_target_os_kernel" = "xwindows32"; then
WINDOWS_ARCH="x86"
elif test "x$app_cv_target_os_kernel" = "xwindows64"; then
WINDOWS_ARCH="amd64"
fi
AC_SUBST(WINDOWS_ARCH)
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
RES_FILES="src/res/gsc_about_dialog.ui \
src/res/gsc_add_device_window.ui \
src/res/gsc_executor_log_window.ui \
src/res/gsc_info_window.ui \
src/res/gsc_main_window.ui \
src/res/gsc_preferences_window.ui \
src/res/gsc_text_window.ui \
AUTHORS.txt LICENSE_gsmartcontrol.txt"
RES_FILES="src/res/gsc_about_dialog.glade \
src/res/gsc_executor_log_window.glade \
src/res/gsc_help_window.glade \
src/res/gsc_info_window.glade \
src/res/gsc_main_window.glade \
src/res/gsc_preferences_window.glade \
src/res/gsc_text_window.glade \
AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt"
# Unfortunately, _SOURCES can't contain substitutions, so we use LIBADD.
RES_LIBADD="" # .o file to put into .a, libres_a_LIBADD, libres_a_DEPENDENCIES.
RES_DIST="" # only files in "res" directory, EXTRA_DIST
RES_TARGETS="" # entry for each file in makefile
RES_GLD_EXT="glade"
if test "$app_cv_gld_reader" = "gtkbuilder"; then
RES_GLD_EXT="ui"
fi
for RES_FILE in $RES_FILES;
do # on separate line for portability
RES_BASE=`echo "$RES_FILE" | sed 's/.*\///'` # "a/b/c" -> "c", "c" -> "c"
@@ -339,14 +341,28 @@ do # on separate line for portability
RES_FILE_CANON="\$(srcdir)/$RES_BASE" # shorter path, better make behaviour
fi
if test "$RES_BASE_EXT" = "ui"; then
RES_LIBADD="$RES_LIBADD $RES_BASE_NOEXT.ui.$ac_objext" # file.ui.o
# We use separate source files for gtkbuilder and libglademm, because
# make dist should be able to bundle both of them separately.
if test "$RES_BASE_EXT" = "glade"; then
RES_LIBADD="$RES_LIBADD $RES_BASE_NOEXT.$RES_GLD_EXT.$ac_objext" # file.ui.o or file.glade.o
# Output both glade and ui rules. This way make dist will be able
# to build cpp files of both of them.
RES_TARGETS="$RES_TARGETS
$RES_BASE_NOEXT.ui.cpp: $RES_FILE_CANON
\"\$(top_srcdir)/file2csource.sh\" \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT.ui.cpp\" \"$RES_BASE_NOEXT\"_ui
gtk-builder-convert \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT\".tmp_ui
\"\$(top_srcdir)/file2csource.sh\" \"$RES_BASE_NOEXT\".tmp_ui \"$RES_BASE_NOEXT.ui.cpp\" \"$RES_BASE_NOEXT\"_ui
"
RES_TARGETS="$RES_TARGETS
$RES_BASE_NOEXT.glade.cpp: $RES_FILE_CANON
\"\$(top_srcdir)/file2csource.sh\" \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT.glade.cpp\" \"$RES_BASE_NOEXT\"_ui
"
else # txt, etc...
SRC_FILE="$RES_BASE.cpp"
RES_LIBADD="$RES_LIBADD $RES_BASE.$ac_objext" # file.txt.o
@@ -378,8 +394,7 @@ _AM_SUBST_NOTMAKE([RES_TARGETS])
ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
-DPACKAGE_SYSCONF_DIR=\"\\\"\$(sysconfdir)\\\"\" \
-DTOP_SRC_DIR=\"\\\"\$(top_srcdir)\\\"\" \
-DHZ_USE_GLOBAL_MACROS=1"
-DTOP_SRC_DIR=\"\\\"\$(top_srcdir)\\\"\""
#CFLAGS="$CFLAGS $ADDITIONAL_FLAGS"
CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
@@ -389,20 +404,20 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
# Add global configuration file. Note: It must be searched in top_builddir
# first (that's why -I is the first flag), for precompiled headers to work.
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src"
# Add pcrecpp too, because its files use #<file> on each other.
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src -I\$(top_srcdir)/src/pcrecpp"
# -include works with gcc, intel, pathscale and sunstudio since 12u1, but doesn't work
# with pgi and older sunstudio (the one in solaris10, for example).
# -include works with gcc, intel and pathscale, but doesn't work with sun and pgi.
# Note: some gcc-frontend-based compilers may have to be added here
# (e.g. gccfss (gcc frontend / sun backend), etc...).
# We use -include only when using pch.
# if test "x$app_cv_compiler_gcc_pch" = "xyes"; then
# if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang" \
# || test "$ax_cv_cxx_compiler_vendor" = "intel" || test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
# # This auto-includes this file when compiling any file. Benefits include pch support.
# ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -include global_macros.h"
# fi
# fi
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel" \
|| test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
# This auto-includes this file when compiling any file. Benefits include pch support.
ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -include global_macros.h"
else # sun, pgi, others...
# This forces the file to be included manually.
ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -DHZ_NO_COMPILER_AUTOINCLUDE"
fi
# our flags must be before all the others
#CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS"
@@ -453,8 +468,6 @@ AC_SUBST(LDFLAGS)
# print them out for debug purposes
AC_MSG_NOTICE([
CXX:
$CXX
CXXFLAGS:
$CXXFLAGS
LDFLAGS:
@@ -474,7 +487,11 @@ AC_SUBST(AUTODIRS)
# -------------------------------------------------------------------------------------
AC_CONFIG_FILES([data/gsmartcontrol.desktop data/gsmartcontrol.appdata.xml data/nsis/distribution.txt data/nsis/gsmartcontrol.nsi debian.dist/changelog src/gsc_winres.rc src/gsmartcontrol.exe.manifest gsmartcontrol.spec version.txt compilation_flags])
AC_CONFIG_FILES([ data/gsmartcontrol.desktop \
data/nsis/distribution.txt data/nsis/gsmartcontrol.nsi \
debian.dist/changelog \
src/gsc_winres.rc \
gsmartcontrol.spec version compilation_flags ])
AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
@@ -482,7 +499,11 @@ AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
# these are all the makefiles to generate
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile src/libdebug/Makefile src/rconfig/Makefile src/rmn/Makefile data/Makefile data/16/Makefile data/22/Makefile data/24/Makefile data/32/Makefile data/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile debian.dist/Makefile])
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile \
src/libdebug/Makefile src/pcrecpp/Makefile src/rconfig/Makefile src/rmn/Makefile \
data/Makefile data/16/Makefile data/22/Makefile data/24/Makefile data/32/Makefile \
data/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile \
debian.dist/Makefile])
AC_OUTPUT
@@ -1,9 +1,9 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 - 2010 Alex Butcher <alex dot butcher 'at' assursys.co.uk>
# (C) 2008 - 2010 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_unlicense.txt file
# (C) 2008 - 2009 Alex Butcher <alex dot butcher 'at' assursys.co.uk>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_whatever.txt file
############################################################################
device="$1";
+3 -3
View File
@@ -1,9 +1,9 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 - 2010 Alex Butcher <alex dot butcher 'at' assursys.co.uk>
# (C) 2008 - 2010 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_unlicense.txt file
# (C) 2008 - 2009 Alex Butcher <alex dot butcher 'at' assursys.co.uk>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_whatever.txt file
############################################################################
if [ "$1" = "-V" ]; then
+2 -10
View File
@@ -13,7 +13,7 @@ uninstall-extra-mans:
# This command is needed if installing with make install (the cache
# file is ignored for rpms).
gtk_update_icon_cache = gtk-update-icon-cache-3.0 -f -t $(datadir)/icons/hicolor
gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
install-data-hook: install-extra-mans update-icon-cache
uninstall-hook: uninstall-extra-mans update-icon-cache
@@ -40,14 +40,6 @@ appdata_DATA = icon_cddvd.png icon_hdd.png
desktopdir = $(datadir)/applications
desktop_DATA = gsmartcontrol.desktop
# Appdata files
metainfodir = $(datadir)/metainfo
metainfo_DATA = gsmartcontrol.appdata.xml
# PolKit files
polkitdir = $(datadir)/polkit-1/actions
polkit_DATA = org.gsmartcontrol.policy
# Application pixmap (fallback icon for desktop files).
# Same as 48/gsmartcontrol.png.
@@ -57,7 +49,7 @@ pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm
# Put everything inside the distribution
EXTRA_DIST = $(appdata_DATA) $(polkit_DATA) gsmartcontrol.ico gsmartcontrol.xpm
EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm
bin_SCRIPTS = gsmartcontrol-root
+29 -89
View File
@@ -1,53 +1,27 @@
#!/usr/bin/env bash
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 - 2014 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Run gsmartcontrol with root, asking for root password first.
# export GSMARTCONTROL_SU to override a su command (e.g. "kdesu -c").
EXEC_BIN="@prefix@/sbin/gsmartcontrol";
EXEC_BIN="@prefix@/bin/gsmartcontrol";
prog_name="gsmartcontrol"
DESKTOP="$1";
# Preserve quotes in arguments
final_args_quoted="";
for i in "$@";do
final_args_quoted="$final_args_quoted \"${i//\"/\\\"}\"";
done;
# This works for --help too
if [ "$DESKTOP" = "" ]; then
DESKTOP="auto"; # default
DESKTOP="auto";
# Compatibility with old syntax:
# gsmartcontrol-root [<desktop> [program_options]]
if [ "$1" == "auto" ] || [ "$1" == "kde" ] || [ "$1" == "gnome" ] || [ "$1" == "other" ]; then
DESKTOP="$1";
shift; # remove $1
else
# New syntax:
# gsmartcontrol-root [--desktop=<auto|kde|gnome|other>] [program_options]
for arg in $*; do
case $arg in
--desktop=*)
DESKTOP="${arg#*=}";
final_args_quoted="${final_args_quoted/\"$arg\"/}";
;;
*)
# unknown option
;;
esac
done
fi
if [ "$DESKTOP" != "auto" ] && [ "$DESKTOP" != "kde" ] && \
elif [ "$DESKTOP" != "auto" ] && [ "$DESKTOP" != "kde" ] && \
[ "$DESKTOP" != "gnome" ] && [ "$DESKTOP" != "other" ]; then
echo "Usage: $0 [--desktop=<auto|kde|gnome|other>] [<${prog_name}_options>]";
echo "Usage: $0 [<auto|kde|gnome|other> [<${prog_name}_options>] ]";
exit 1;
fi
shift; # remove $1
# Auto-detect current desktop if auto was specified.
@@ -67,16 +41,11 @@ fi
# echo $DESKTOP;
# They're basically the same, only the order is different.
# pkexec is for PolKit.
# sux requires xterm to ask for the password.
# xdg-su is basically like this script, except worse :)
# su-to-root is a debian/ubuntu official method (although gksu is available).
gnome_sus="pkexec su-to-root gnomesu gksu kdesu beesu xdg-su sux";
kde_sus="pkexec su-to-root kdesu gnomesu gksu beesu xdg-su sux";
gnome_sus="gnomesu gksu kdesu xdg-su sux";
kde_sus="kdesu gnomesu gksu xdg-su sux";
other_sus="$gnome_sus";
@@ -91,20 +60,18 @@ elif [ "$DESKTOP" = "other" ]; then
candidates="$other_sus";
fi
if [ "$GSMARTCONTROL_SU" = "" ]; then
for subin in $candidates; do
which $subin &>/dev/null
if [ $? -eq 0 ]; then
found_su="$subin";
break;
fi
done
if [ "$found_su" = "" ]; then
xmessage "Error launching ${prog_name}: No suitable su mechanism found.
Try installing PolKit, kdesu, gnomesu, gksu, beesu or sux first.";
exit 1;
for subin in $candidates; do
which $subin &>/dev/null
if [ $? -eq 0 ]; then
found_su="$subin";
break;
fi
done
if [ "$found_su" = "" ]; then
xmessage "Error launching ${prog_name}: No suitable su mechanism found.
Try installing kdesu, gnomesu, gksu or sux first.";
exit 1;
fi
@@ -115,11 +82,7 @@ fi
# Add these directories _before_ existing PATH, so that the user is not
# tricked into running it from some other path (we're running as root with
# the user's env after all).
# Add @prefix@/sbin as well (freebsd seems to require it).
# Note that beesu won't show a GUI login box if /usr/sbin is before /usr/bin,
# so add it first as well.
EXTRA_PATHS="/usr/bin:/usr/sbin:/usr/local/sbin:@prefix@/sbin";
export PATH="$EXTRA_PATHS:$PATH"
export PATH="/usr/sbin:/usr/local/sbin:$PATH"
# echo $found_su;
@@ -127,43 +90,20 @@ export PATH="$EXTRA_PATHS:$PATH"
# Examples:
# gnomesu -c 'gsmartcontrol --no-scan'
# kdesu -c 'gsmartcontrol --no-scan'
# beesu -P 'gsmartcontrol --no-scan'
# su-to-root -X -c 'gsmartcontrol --no-scan'
# xterm -e sux -c 'gsmartcontrol --no-scan' # sux asks for password in a terminal
full_cmd="";
if [ "$GSMARTCONTROL_SU" != "" ]; then
full_cmd="$GSMARTCONTROL_SU '$EXEC_BIN $final_args_quoted'";
elif [ "$found_su" = "pkexec" ]; then
if [ "$GDK_SCALE" != "" ]; then
final_args_quoted="$final_args_quoted --gdk-scale='$GDK_SCALE'"
fi
if [ "$GDK_DPI_SCALE" != "" ]; then
final_args_quoted="$final_args_quoted --gdk-dpi-scale='$GDK_DPI_SCALE'"
fi
full_cmd="pkexec --disable-internal-agent $EXEC_BIN $final_args_quoted";
elif [ "$found_su" = "sux" ]; then
full_cmd="xterm -e sux -c '$EXEC_BIN $final_args_quoted'";
full_cmd=""
if [ "$found_su" = "sux" ]; then
full_cmd="xterm -e sux -c '$EXEC_BIN $@'"
elif [ "$found_su" = "gksu" ]; then
full_cmd="$found_su '$EXEC_BIN $final_args_quoted'";
elif [ "$found_su" = "beesu" ]; then
full_cmd="$found_su -P '$EXEC_BIN $final_args_quoted'";
elif [ "$found_su" = "su-to-root" ]; then
full_cmd="$found_su -X -c '$EXEC_BIN $final_args_quoted'";
full_cmd="$found_su '$EXEC_BIN $@'"
else # gnomesu, kdesu, xdg-su
full_cmd="$found_su -c '$EXEC_BIN $final_args_quoted'";
full_cmd="$found_su -c '$EXEC_BIN $@'"
fi
# echo $full_cmd
eval $full_cmd
+9 -16
View File
@@ -1,16 +1,16 @@
.TH GSmartControl "1" "" "gsmartcontrol " "User Commands"
.SH NAME
GSmartControl \- Hard disk drive and SSD health inspection tool
GSmartControl \- Hard disk drive health inspection tool
.SH SYNOPSIS
\fBgsmartcontrol\fP [OPTIONS]
\fBgsmartcontrol\-root\fP [--desktop=\fI<desktop>\fP] [OPTIONS]
\fBgsmartcontrol\-root\fP [\fI<desktop>\fP [OPTIONS]]
.SH DESCRIPTION
\fBGSmartControl\fP is a graphical user interface for smartctl (from
smartmontools), which is a tool for querying and controlling SMART
(Self-Monitoring, Analysis, and Reporting Technology) data on modern hard disk
and solid-state drives. It allows you to inspect the drive's SMART data to
determine its health, as well as run various tests on it.
drives. It allows you to inspect the drive's SMART data to determine its
health, as well as run various tests on it.
.PP
This manual page documents briefly the \fBgsmartcontrol\fP and
\fBgsmartcontrol\-root\fP commands.
@@ -37,7 +37,7 @@ Show logging options
.SS "Application Options:"
.TP
\fB\-l\fP, \fB\-\-no\-locale\fP
Don't use system locale
Disable locale
.TP
\fB\-V\fP, \fB\-\-version\fP
Display version information
@@ -49,17 +49,10 @@ Don't scan devices on startup
Don't hide non\-identity tabs when SMART is disabled. Useful for debugging.
.TP
\fB\-\-add\-virtual\fP
Load smartctl data from file, creating a virtual drive. You
can specify this option multiple times.
Load smartctl data from file, creating a virtual drive
.TP
\fB\-\-add\-device\fP
Add this device to device list. The format of the device is
\fI<device>::<type>::<extra_args>\fP, where type and extra_args are optional.
This option is useful with \fB\-\-no\-scan\fP to list certain drives only. You
can specify this option multiple times. Example:\fR
.nf
\-\-add\-device /dev/sda \-\-add\-device /dev/twa0::3ware,2 \-\-add\-device '/dev/sdb::::-T permissive'
.fi
Add this device to device list. Useful with \fB\-\-no\-scan\fP to list certain drives only.
.TP
\fB\-\-display\fP=\fIDISPLAY\fP
X display to use
@@ -74,8 +67,8 @@ Disable logging; same as \fB\-\-verbosity\-level\fP 0
Set verbosity level [0\-5]
.PP
.SH COPYRIGHT
Copyright \(co 2008 \- 2012 Alexander Shaduri <ashaduri \'at\' gmail.com>
Copyright \(co 2008 \- 2009 Alexander Shaduri <ashaduri \'at\' gmail.com>
.PP
.SH AUTHOR
This manual page was originally written by Giuseppe Iuculano
<giuseppe@iuculano.it> for the Debian project.
<giuseppe@iuculano.it>, for the Debian project.
-34
View File
@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2017 - 2022 GSmartControl developers -->
<component type="desktop">
<id>gsmartcontrol</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0 and GPL-3.0</project_license>
<name>GSmartControl</name>
<summary>Hard Disk and SSD Health Inspection</summary>
<description>
<p>
GSmartControl is a graphical user interface for smartctl, which is a tool for
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
Technology) data in hard disk and solid-state drives. It allows you to inspect
the drive's SMART data to determine its health, as well as run various tests
on it.
</p>
</description>
<launchable type="desktop-id">gsmartcontrol.desktop</launchable>
<url type="homepage">https://gsmartcontrol.shaduri.dev</url>
<screenshots>
<screenshot type="default">
<caption>Main window</caption>
<image>https://gsmartcontrol.sourceforge.io/home/images/main_ok.png</image>
</screenshot>
<screenshot>
<caption>Drive identity information</caption>
<image>https://gsmartcontrol.sourceforge.io/home/images/info_identity.png</image>
</screenshot>
<screenshot>
<caption>Attribute list of a failing drive</caption>
<image>https://gsmartcontrol.sourceforge.io/home/images/info_failing.png</image>
</screenshot>
</screenshots>
</component>
+5 -5
View File
@@ -8,12 +8,9 @@ Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
GenericName=Hard Disk and SSD Health Inspection
GenericName[fr]=Inspecteur de santé de disque dur et de SSD
GenericName=Hard Disk Health Inspection
# tooltip
Comment=Monitor and control SMART data on hard disk and solid-state drives
Comment[fr]=Surveille et contrôle les données SMART des disques durs et des SSD
Comment=Monitor and control SMART data on hard disks
# If it's a name only, it looks for "name.[png|xpm]" file in
# $XDG_DATA_DIRS/icons and /usr/share/pixmaps.
@@ -27,3 +24,6 @@ Icon=gsmartcontrol
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol-root"
# Build service rpmlint gives errors without this
X-SuSE-translate=false
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+17 -9
View File
@@ -1,23 +1,31 @@
GSmartControl - Hard disk drive and SSD health inspection tool
GSmartControl - Hard disk drive health inspection tool
Version @VERSION@
The latest version of this distribution, as well as the source code of
GSmartControl, can be found at
https://gsmartcontrol.shaduri.dev
http://gsmartcontrol.berlios.de
and
https://sourceforge.net/projects/gsmartcontrol/files/
http://developer.berlios.de/project/showfiles.php?group_id=10075
You need GTK+ for Windows Runtime package to use this program. You can get one
at http://gtk-win.sf.net (you must download at least version 2.14.4-2008-11-11
of gtk2-runtime).
This distribution includes:
* GSmartControl binary (gsmartcontrol.exe) and associated resources.
The binary was compiled against Gtkmm (http://www.gtkmm.org/), GTK+
(http://gtk.org/) and related libraries, and PCRE (http://www.pcre.org/).
* GSmartControl binary (gsmartcontrol.exe) and associated resources. The
binary was compiled for 32-bit Windows, and should work on both 32-bit and
64-bit Windows.
* Smartctl binaries (from smartmontools 6.6, http://smartmontools.sf.net/)
- smartctl-nc.exe (compiled without console output), smartctl.exe (compiled
with console output), update-smart-drivedb.exe (drive database updater).
* Gtkmm (http://www.gtkmm.org/) dlls.
* PCRE (http://www.pcre.org/) dll.
* Smartctl binaries (from smartmontools 5.38, http://smartmontools.sf.net/) -
smartctl-nc.exe (compiled without console output), smartctl.exe (compiled with
console output).
The combined distribution is multi-licensed under GNU GPL versions 2 and 3.
+140 -168
View File
@@ -1,32 +1,26 @@
; NSIS2 Script
; by Alexander Shaduri <ashaduri 'at' gmail.com>.
; Compatible with NSIS Unicode 2.45.
; Public Domain
; Compatible with NSIS 2.40
Unicode True
!define PRODUCT_VERSION "@VERSION@"
!define PRODUCT_NAME "GSmartControl"
!define PRODUCT_NAME_SMALL "gsmartcontrol"
!define PRODUCT_PUBLISHER "Alexander Shaduri"
!define PRODUCT_WEB_SITE "https://gsmartcontrol.shaduri.dev"
!define PRODUCT_WEB_SITE "http://gsmartcontrol.berlios.de"
;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\AppMainExe.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define REGISTRY_APP_PATHS "Software\Microsoft\Windows\CurrentVersion\App Paths"
!include "FileFunc.nsh" ; GetOptions
; --------------- General Settings
; This is needed for proper start menu item manipulation (for all users) in vista
RequestExecutionLevel admin
; This compressor gives us the best results
SetCompressor /SOLID lzma
@@ -37,12 +31,13 @@ CRCCheck On
Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION}
; Output File Name
OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-@WINDOWS_SUFFIX@.exe"
OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}.exe"
; The Default Installation Directory:
; Try to install to the same directory as runtime.
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
;The Default Installation Directory
; try to install to the same directory as runtime.
InstallDir "$PROGRAMFILES\${PRODUCT_NAME_SMALL}"
; If already installed, try here
InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory"
@@ -58,9 +53,8 @@ ShowUnInstDetails show
; MUI 2.0 compatible install
!include "MUI2.nsh"
!include "InstallOptions.nsh" ; section description macros
; Backgound Colors. uncomment to enable fullscreen.
;Backgound Colors. uncomment to enable fullscreen.
; BGGradient 0000FF 000000 FFFFFF
; MUI Settings
@@ -69,22 +63,22 @@ ShowUnInstDetails show
!define MUI_UNICON "nsi_uninstall.ico"
; Things that need to be extracted on first (keep these lines before any File command!).
; Only useful for BZIP2 compression.
;!insertmacro MUI_RESERVEFILE_LANGDLL
;Things that need to be extracted on first (keep these lines before any File command!)
;Only useful for BZIP2 compression
!insertmacro MUI_RESERVEFILE_LANGDLL
;!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
; Language Selection Dialog Settings
;!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
;!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
;!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language"
!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language"
!define LICENSE_FILE "doc\distribution.txt"
; Pages to show during installation
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}"
!insertmacro MUI_PAGE_LICENSE "doc\distribution.txt"
;!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
@@ -112,91 +106,139 @@ ShowUnInstDetails show
LangString TEXT_IO_TITLE ${LANG_ENGLISH} "GSmartControl"
var install_option_removeold ; uninstall the old version first (if present): yes (default), no.
; --------------- START INSTALLATION
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
Push 2 ;F ;22 = number of languages, F = change font
;Language selection
LangDLL::LangDialog "Installer Language" "Please select a language."
Pop $LANGUAGE
StrCmp $LANGUAGE "cancel" 0 +2
Abort
; detect previous installation
ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "UninstallString"
StrCmp $R0 "" no_prev
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick 'OK' to remove the \
previous version or 'Cancel' to continue anyway." \
IDOK uninst_product
; Abort
goto no_prev ; if cancel was pressed, continue as if no old installation.
;Run the uninstaller
uninst_product:
ClearErrors
ExecWait '$R0 _?=$INSTDIR' ;Do not copy the uninstaller to a temp file
IfErrors cannot_uninst_product
;You can either use Delete /REBOOTOK in the uninstaller or add some code
;here to remove the uninstaller. Use a registry key to check
;whether the user has chosen to uninstall. If you are using an uninstaller
;components page, make sure all sections are uninstalled.
cannot_uninst_product:
no_prev: ; no old installation found, all ok
; detect GTK installation (any of available versions)
ReadRegStr $R0 HKLM "SOFTWARE\GTK\2.0" "DllPath"
StrCmp $R0 "" no_gtk
goto have_gtk
no_gtk:
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"GTK2-Runtime is not installed. This product needs it to function properly.$\n\
Please install GTK2-Runtime from http://gtk-win.sf.net/ first.$\n$\n\
Click 'Cancel' to abort the installation \
or 'OK' to continue anyway." \
IDOK have_gtk
Abort
have_gtk:
; do nothing
FunctionEnd
; ----------------- INSTALLATION TYPES
; InstType "Recommended"
; InstType "Full"
; InstType "Minimal"
InstType "Full"
;InstType "Minimal"
Section "!GSmartControl" SecMain
SectionIn 1 RO
SetShellVarContext all ; use all user variables as opposed to current user
SetOutPath "$INSTDIR"
SetOverwrite Off ; don't overwrite the config file
; File "gsmartcontrol.conf"
SetOverwrite On
File *.exe
File *.dll
File gsmartcontrol.ico
File icon_*.png
File drivedb.h
Section "Main" SecMain
SectionIn 1
SetOutPath "$INSTDIR"
; Include the "doc" directory completely.
File /r doc
SetOverwrite Off ; don't overwrite the config file
; File "gsmartcontrol.conf"
; GTK and stuff
File /r etc
File /r share
File /r lib
SetOverwrite On
File gsmartcontrol.exe
File gsmartcontrol.ico
File icon_*.png
File *.dll
File smartctl-nc.exe
File smartctl.exe
; Add Shortcuts (this inherits the exe's run permissions)
CreateShortCut "$SMPROGRAMS\GSmartControl.lnk" "$INSTDIR\gsmartcontrol.exe" "" \
"$INSTDIR\gsmartcontrol.ico" "" SW_SHOWNORMAL "" "GSmartControl - Hard disk drive and SSD health inspection tool"
File /r doc
; set a special path for this exe, as GTK may not be in a global path.
ReadRegStr $R3 HKLM "SOFTWARE\GTK\2.0" "DllPath"
WriteRegStr HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe" "Path" "$R3"
;Add Shortcuts
CreateShortCut "$SMPROGRAMS\GSmartControl.lnk" "$INSTDIR\gsmartcontrol.exe" "" "$INSTDIR\gsmartcontrol.ico" 0
SectionEnd
; Executed on installation start
Function .onInit
SetShellVarContext all ; use all user variables as opposed to current user
${GetOptions} "$CMDLINE" "/removeold=" $install_option_removeold
Call PreventMultipleInstances
Call DetectPrevInstallation
FunctionEnd
; ------------------ POST INSTALL
!define TEMP $R0
Section -post
SetShellVarContext all ; use all user variables as opposed to current user
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR"
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR"
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "Alexander Shaduri"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gsmartcontrol_uninst.exe"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\gsmartcontrol.ico"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoModify" 1
WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoRepair" 1
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gsmartcontrol_uninst.exe"
; We don't need this, MUI takes care for us
; WriteRegStr HKCU "Software\${PRODUCT_NAME}" "Installer Language" $sUAGE
; We don't need this, MUI takes care for us
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "Installer Language" $LANGUAGE
; write out uninstaller
WriteUninstaller "$INSTDIR\gsmartcontrol_uninst.exe"
; uninstall shortcut
; CreateDirectory "$SMPROGRAMS\GSmartControl"
; CreateShortCut "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" "$INSTDIR\gsmartcontrol_uninst.exe" "" ""
; uninstall shortcut
;CreateDirectory "$SMPROGRAMS\GSmartControl"
;CreateShortCut "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" "$INSTDIR\gsmartcontrol_uninst.exe" "" ""
; write out uninstaller
WriteUninstaller "$INSTDIR\gsmartcontrol_uninst.exe"
SectionEnd ; post
@@ -209,113 +251,43 @@ SectionEnd ; post
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." /SD IDOK
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
FunctionEnd
Section Uninstall
SetShellVarContext all ; use all user variables as opposed to current user
SetAutoClose false
; add delete commands to delete whatever files/registry keys/etc you installed here.
Delete "$INSTDIR\gsmartcontrol_uninst.exe"
SetAutoClose false
DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}"
DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}"
; add delete commands to delete whatever files/registry keys/etc you installed here.
Delete "$INSTDIR\gsmartcontrol_uninst.exe"
Delete "$INSTDIR\*.exe"
Delete "$INSTDIR\*.dll"
Delete "$INSTDIR\gsmartcontrol.ico"
Delete "$INSTDIR\icon_*.png"
DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}"
DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}"
; update-smart-drivedb may leave these
Delete "$INSTDIR\drivedb.h"
Delete "$INSTDIR\drivedb.h.*"
Delete "$INSTDIR\gsmartcontrol.exe"
Delete "$INSTDIR\gsmartcontrol.ico"
Delete "$INSTDIR\icon_*.png"
Delete "$INSTDIR\*.dll"
Delete "$INSTDIR\smartctl-nc.exe"
Delete "$INSTDIR\smartctl.exe"
RMDir /r "$INSTDIR\doc"
RMDir /r "$INSTDIR\doc"
; GTK and stuff
RMDir /r "$INSTDIR\etc"
RMDir /r "$INSTDIR\share"
RMDir /r "$INSTDIR\lib"
RMDir "$INSTDIR" ; only if empty
; clean up generated stuff
Delete "$INSTDIR\*stdout.txt"
Delete "$INSTDIR\*stderr.txt"
DeleteRegKey HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe"
RMDir "$INSTDIR" ; only if empty
Delete "$SMPROGRAMS\GSmartControl.lnk"
; Delete "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk"
Delete "$SMPROGRAMS\GSmartControl.lnk"
;Delete "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk"
SectionEnd ; end of uninstall section
; --------------- Helpers
; Detect previous installation
Function DetectPrevInstallation
; if /removeold=no option is given, don't check anything.
StrCmp $install_option_removeold "no" old_detect_done
SetShellVarContext all ; use all user variables as opposed to current user
push $R0
; detect previous installation
ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "UninstallString"
StrCmp $R0 "" old_detect_done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
previous version or `Cancel` to continue anyway." \
/SD IDOK IDOK old_uninst
; Abort
goto old_detect_done
; Run the old uninstaller
old_uninst:
ClearErrors
IfSilent old_silent_uninst old_nosilent_uninst
old_nosilent_uninst:
ExecWait '$R0'
goto old_uninst_continue
old_silent_uninst:
ExecWait '$R0 /S _?=$INSTDIR'
old_uninst_continue:
IfErrors old_no_remove_uninstaller
; You can either use Delete /REBOOTOK in the uninstaller or add some code
; here to remove to remove the uninstaller. Use a registry key to check
; whether the user has chosen to uninstall. If you are using an uninstaller
; components page, make sure all sections are uninstalled.
old_no_remove_uninstaller:
old_detect_done: ; old installation not found, all ok
pop $R0
FunctionEnd
; Prevent running multiple instances of the installer
Function PreventMultipleInstances
Push $R0
System::Call 'kernel32::CreateMutexA(i 0, i 0, t ${PRODUCT_NAME}) ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." /SD IDOK
Abort
Pop $R0
FunctionEnd
; eof
; eof
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="org.gsmartcontrol">
<message>Authentication is required to run GSmartControl</message>
<icon_name>gsmartcontrol</icon_name>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/gsmartcontrol</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>
+1 -1
View File
@@ -1 +1 @@
9
5
+9 -6
View File
@@ -1,18 +1,21 @@
Source: gsmartcontrol
Section: utils
Priority: extra
Homepage: https://gsmartcontrol.shaduri.dev
Homepage: http://gsmartcontrol.berlios.de/
Maintainer: Alexander Shaduri <ashaduriREMOVETHIS@gmail.com>
# technically, we don't need libglademm if gtkmm >= 2.12, but it won't get
# into Depends if it's not needed, so let's leave it here.
Build-Depends: debhelper (>= 5), autotools-dev,
libpcre3-dev, libgtkmm-3.0-dev (>= 3.4.0)
libpcre3-dev, libgtkmm-2.4-dev (>= 2.6.0), libglademm-2.4-dev
Standards-Version: 3.7.3
Package: gsmartcontrol
Architecture: any
Depends: ${shlibs:Depends}, smartmontools (>= 5.43), xterm, menu
Description: Hard disk drive and SSD health inspection tool
#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools
Depends: ${shlibs:Depends}, smartmontools
Description: Hard disk drive health inspection tool
GSmartControl is a graphical user interface for smartctl (from smartmontools
package), which is a tool for querying and controlling SMART
(Self-Monitoring, Analysis, and Reporting Technology) data on modern hard
disk and solid-state drives. It allows you to inspect the drive's SMART data
to determine its health, as well as run various tests on it.
disk drives. It allows you to inspect the drive's SMART data to determine its
health, as well as run various tests on it.
+3 -3
View File
@@ -1,11 +1,11 @@
This package was debianized by Alexander Shaduri <ashaduri 'at' gmail.com> on
Sat, 15 Nov 2008 00:12:04 +0400.
It was downloaded from <https://gsmartcontrol.shaduri.dev>
It was downloaded from <http://gsmartcontrol.berlios.de/>
Copyright:
Copyright (C) 2008 - 2022 Alexander Shaduri <ashaduri 'at' gmail.com>
Copyright (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License:
@@ -27,6 +27,6 @@ On Debian systems, the complete text of the GNU General
Public Licenses can be found in `/usr/share/common-licenses/GPL-2' and
`/usr/share/common-licenses/GPL-3'.
The Debian packaging is (C) 2008 - 2022 Alexander Shaduri <ashaduri 'at' gmail.com> and
The Debian packaging is (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com> and
is licensed under the GPLv2 and GPLv3, see above.
+1 -1
View File
@@ -1,4 +1,4 @@
?package(gsmartcontrol):needs="X11" section="Applications/System/Hardware"\
title="GSmartControl" command="/usr/bin/gsmartcontrol-root"\
longtitle="Hard Disk and SSD Health Inspection"\
longtitle="Hard Disk Health Inspection"\
icon="/usr/share/pixmaps/gsmartcontrol.xpm"
+1 -1
View File
@@ -85,7 +85,7 @@ binary-arch: build install
# dh_installinit
# dh_installcron
# dh_installinfo
dh_installman
# dh_installman
dh_link
dh_strip
dh_compress
-35
View File
@@ -1,35 +0,0 @@
/**************************************************************************
Copyright:
(C) 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
// This file is for generating documentation only. Do NOT include it.
/// \file
/// \author Alexander Shaduri
/**
\mainpage GSmartControl Index
\section intro_section Introduction
This is GSmartControl internal documentation.
*/
// Group definitions and titles.
/**
\addtogroup applib Application-Specific Components
\addtogroup applib_tests Tests of Application-Specific Components
\addtogroup hz HZ Common System Components
\addtogroup hz_tests Tests for HZ Common System Components
\addtogroup libdebug LibDebug Flexible Debug Logging
\addtogroup libdebug_tests Tests for LibDebug Flexible Debug Logging
\addtogroup rmn Resource Manager
\addtogroup rmn_tests Tests for Resource Manager
\addtogroup rconfig Resource Manager-Based Configuration
\addtogroup rconfig_tests Tests for Resource Manager-Based Configuration
\addtogroup gsc GSmartControl Application
*/
+2 -7
View File
@@ -1,7 +1,7 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -24,12 +24,7 @@ fi
in_file="$1";
out_file="$2";
sym_name="$3";
size="";
if [[ $OSTYPE == 'darwin'* ]]; then
size=`stat "-f%z" "$in_file"`
else
size=`stat -c "%s" "$in_file"`
fi
size=`stat -c "%s" "$in_file"`
# header. extern means "global" in this context (needed because const
-3
View File
@@ -1,3 +0,0 @@
[Project]
Manager=KDevCustomBuildSystem
Name=gsmartcontrol
+17 -68
View File
@@ -22,7 +22,7 @@
<kdevautoproject>
<general>
<activetarget>src/gsmartcontrol</activetarget>
<useconfiguration>open64</useconfiguration>
<useconfiguration>debuggy</useconfiguration>
</general>
<run>
<mainprogram>/10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy</mainprogram>
@@ -76,7 +76,7 @@
<f77flags/>
</optimized>
<debuggy>
<configargs>--enable-debug-options --enable-tests</configargs>
<configargs>--disable-user-flags --enable-debug-options --enable-tests</configargs>
<builddir>debuggy</builddir>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
@@ -87,8 +87,8 @@
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompilerbinary>gcc-4.4</ccompilerbinary>
<cxxcompilerbinary>g++-4.4</cxxcompilerbinary>
<ccompilerbinary/>
<cxxcompilerbinary/>
<f77compilerbinary/>
<f77flags/>
</debuggy>
@@ -102,8 +102,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>icc64</ccompilerbinary>
<cxxcompilerbinary>icpc64</cxxcompilerbinary>
<ccompilerbinary>icc</ccompilerbinary>
<cxxcompilerbinary>icpc</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -111,7 +111,7 @@
</icc>
<win32>
<envvars/>
<configargs>--with-windows-gtk-name=gtk2-runtime-2.16.6-2010-02-24-ash --disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH&quot;</configargs>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests --target=i386-mingw32 --host=i386-mingw32 --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<builddir>win32</builddir>
<topsourcedir/>
<cppflags/>
@@ -119,8 +119,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>i386-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-pc-mingw32-g++</cxxcompilerbinary>
<ccompilerbinary>i386-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -128,21 +128,21 @@
</win32>
<suncc>
<envvars>
<envvar value="/opt/sun/sunstudio/bin:$PATH" name="PATH" />
<envvar value="/opt/sun/sunstudio12/bin:$PATH" name="PATH" />
</envvars>
<configargs>--enable-tests PKG_CONFIG_PATH=&quot;/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig&quot; PKG_CONFIG_LIBDIR=&quot;&quot;</configargs>
<configargs>--enable-tests</configargs>
<builddir>suncc</builddir>
<topsourcedir/>
<cppflags/>
<ldflags>-R/opt/sun/target64/lib64 -L/opt/sun/target64/lib64</ldflags>
<ldflags>-s</ldflags>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>suncc</ccompilerbinary>
<cxxcompilerbinary>sunCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-errwarn=%all -I/opt/sun/target64/include</cflags>
<cxxflags>-I/opt/sun/target64/include</cxxflags>
<cflags>-errwarn=%all</cflags>
<cxxflags>-DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<f77flags/>
</suncc>
<portland>
@@ -164,7 +164,7 @@
</portland>
<win32-debug>
<envvars/>
<configargs>--disable-user-flags --enable-debug-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH&quot;</configargs>
<configargs>--disable-user-flags --enable-debug-options --enable-tests --target=i386-mingw32 --host=i386-mingw32 --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<builddir>win32-debug</builddir>
<topsourcedir/>
<cppflags/>
@@ -172,64 +172,13 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>i386-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-pc-mingw32-g++</cxxcompilerbinary>
<ccompilerbinary>i386-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</win32-debug>
<win64-debug>
<envvars/>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=x86_64-pc-mingw32 PKG_CONFIG_PATH=/cross/w64/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH&quot;</configargs>
<builddir>win64-debug</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>x86_64-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>x86_64-pc-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</win64-debug>
<default>
<envvars/>
<configargs/>
<builddir/>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary/>
<cxxcompilerbinary/>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</default>
<open64>
<envvars/>
<configargs>--enable-tests</configargs>
<builddir>open64</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>/opt/open64/bin/opencc</ccompilerbinary>
<cxxcompilerbinary>/opt/open64/bin/openCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags>-Wall</cxxflags>
<f77flags/>
</open64>
</configurations>
<make>
<envvars>
+87 -53
View File
@@ -1,33 +1,46 @@
# This spec file is for openSUSE Build Service.
# Supported distributions: openSUSE, Fedora, CentOS, RHEL.
Name: gsmartcontrol
Version: @VERSION@
Release: 0
License: GPL-2.0 AND GPL-3.0
Url: https://gsmartcontrol.shaduri.dev
Version: @VERSION@
Release: 1as
License: GPLv2, GPLv3
Url: http://gsmartcontrol.berlios.de
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
# Packager: Alexander Shaduri <ashaduri 'at' gmail.com>
Source: http://sourceforge.net/projects/gsmartcontrol/files/%{version}/%{name}-%{version}.tar.bz2
Packager: Alexander Shaduri <ashaduri 'at' gmail.com>
Source: gsmartcontrol-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Summary: GSmartControl - Hard Disk Drive and SSD Health Inspection Tool
Summary: GSmartControl - Hard Disk Health Inspection Tool
%if 0%{?mandriva_version}
Group: System/Kernel and hardware
%else
Group: Hardware/Other
%endif
# Empty debug packages cause errors in new RPM. Disable them.
%global debug_package %{nil}
# Support build service rpmlint. Disabled - the build service
# stopped recognizing the flag for some architectures.
# %if 0%{?opensuse_bs} && 0%{?suse_version} >= 1010
# Support: rpmlint-bs
# %endif
# Dependencies for various distributions. The actual deps are:
# smartmontools, pcre, (gtkmm2 >= 2.12 || (gtkmm2 >= 2.6.0 && libglademm >= 2.4.0))
# (with the respective -devel packages of the libraries for build requirements).
# For non-specified distributions we don't specify any dependencies to avoid errors.
# For mandriva, these contain the su programs.
# Suggests: libgnomesu, gksu, kdebase-progs
# SUSE / OpenSUSE. SLES also defines the correct suse_version.
%if 0%{?suse_version}
Requires: smartmontools >= 5.43, polkit, bash, xterm
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm3-devel >= 3.4.0
BuildRequires: update-desktop-files
BuildRequires: fdupes
%if 0%{?%suse_version} >= 1030
Requires: smartmontools, pcre, gtkmm2 >= 2.12.0
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.12.0
%else
Requires: smartmontools, pcre, gtkmm2 >= 2.6.0, libglademm >= 2.4.0
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.6.0, libglademm-devel >= 2.4.0
%endif
%endif
@@ -35,8 +48,29 @@ BuildRequires: fdupes
# Fedora, CentOS, RHEL
%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version}
Requires: smartmontools >= 5.43, polkit, bash, xterm
BuildRequires: gcc-c++, pcre-devel, gtkmm30-devel >= 3.4.0
# I'm not sure about centos and rhel versions
%if 0%{?fedora_version} >= 8 || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
Requires: smartmontools, pcre, gtkmm24 >= 2.12.0
BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.12.0
%else
Requires: smartmontools, pcre, gtkmm24 >= 2.6.0, libglademm24 >= 2.4.0
BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm24-devel >= 2.4.0
%endif
%endif
# Mandriva. Check the latest official spec file at
# http://svn.mandriva.com/cgi-bin/viewvc.cgi/packages/cooker/gsmartcontrol/current/
%if 0%{?mandriva_version}
%if 0%{?mandriva_version} >= 2008
Requires: smartmontools, libpcre0, gtkmm2.4 >= 2.12.0
BuildRequires: gcc-c++, gcc-cpp, pcre-devel, gtkmm2.4-devel >= 2.12.0, desktop-file-utils
%else
Requires: smartmontools, libpcre0, gtkmm2.4 >= 2.6.0, libglademm2.4 >= 2.4.0
BuildRequires: gcc-c++, gcc-cpp, pcre-devel, gtkmm2.4-devel >= 2.6.0, libglademm2.4-devel >= 2.4.0, desktop-file-utils
%endif
%endif
@@ -44,45 +78,40 @@ BuildRequires: gcc-c++, pcre-devel, gtkmm30-devel >= 3.4.0
%description
GSmartControl is a graphical user interface for smartctl, which is a tool for
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
Technology) data in hard disk and solid-state drives. It allows you to inspect
the drive's SMART data to determine its health, as well as run various tests
on it.
Technology) data in hard disk drives. It allows you to inspect the drive's
SMART data to determine its health, as well as run various tests on it.
%prep
%setup -q
%configure
%configure --enable-optimize-options=auto
%build
make %{?_smp_mflags}
# Set up for parallel builds
numprocs=`egrep -c ^cpu[0-9]+ /proc/stat || :`
if [ "$numprocs" = "0" ]; then
numprocs=1
fi
make -j$numprocs
# make
%install
# %%makeinstall
# %makeinstall
make DESTDIR=%buildroot install-strip
# Remove the icon cache file "make install" generates, to avoid package conflicts.
rm -f $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/icon-theme.cache
%if 0%{?suse_version}
%suse_update_desktop_file -n %{name}
# There are some png file duplicates, hardlink them.
%fdupes
# We install icons, so this is needed.
%if 0%{?suse_version} >= 1140
%post
%icon_theme_cache_post
%postun
%icon_theme_cache_postun
%endif
# endif suse
%if 0%{?mandriva_version}
desktop-file-install --vendor="" \
--remove-category="Application" \
--add-category="System" \
--add-category="X-MandrivaLinux-System-Configuration-Hardware;Settings;HardwareSettings" \
--dir $RPM_BUILD_ROOT%{_datadir}/applications $RPM_BUILD_ROOT%{_datadir}/applications/%{name}.desktop
%endif
@@ -93,23 +122,28 @@ rm -rf %buildroot
%files
%defattr(-,root,root)
%attr(0755,root,root) %{_bindir}/gsmartcontrol-root
%attr(0755,root,root) %{_sbindir}/gsmartcontrol
# %%attr(0644,root,root) %%config(noreplace) %%{_sysconfdir}/*
%attr(0755,root,root) %{_bindir}/*
# %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
%doc %{_datadir}/doc/gsmartcontrol
%doc %{_mandir}/man1/*
%{_datadir}/gsmartcontrol
# %%{_datadir}/gsmartcontrol/*
# %{_datadir}/gsmartcontrol/*
%{_datadir}/applications/*.desktop
%{_datadir}/metainfo
%{_datadir}/metainfo/gsmartcontrol.appdata.xml
%{_datadir}/polkit-1
%{_datadir}/polkit-1/actions
%{_datadir}/polkit-1/actions/org.gsmartcontrol.policy
%{_datadir}/icons/*
%{_datadir}/pixmaps/*
%changelog
* Fri Nov 14 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
- Updated .spec file to incorporate changes from the official Mandriva package.
* Mon Oct 06 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
- Updated .spec file to support OpenSUSE < 10.3, Fedora and friends,
Mandriva (for OpenSUSE Build Service).
* Mon Sep 15 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
- Initial .spec file.
+10 -15
View File
@@ -1,10 +1,10 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
SUBDIRS = libdebug hz rmn rconfig applib res
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
if TARGET_OS_KERNEL_FAMILY_WINDOWS
if TARGET_OS_KERNEL_WINDOWS
WINRES_OBJ = gsc_winres_win32.o
endif
@@ -16,24 +16,19 @@ gsc_winres_win32.o: $(top_srcdir)/data/gsmartcontrol.ico gsc_winres.rc
mostlyclean-local:
rm -f gsmartcontrol.ico
noinst_DATA = gsc_winres.rc.in gsmartcontrol.exe.manifest.in
noinst_DATA = gsc_winres.rc.in
# if TARGET_OS_KERNEL_FAMILY_WINDOWS
# WIN_MAIN_SRC = win_main.cpp
# endif
sbin_PROGRAMS = gsmartcontrol
bin_PROGRAMS = gsmartcontrol
gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/res/libres.a $(WINRES_OBJ)
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_add_device_window.h gsc_executor_error_dialog.h \
gsc_executor_log_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_executor_error_dialog.h \
gsc_executor_log_window.h gsc_help_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \
gsc_main_window_iconview.h gsc_preferences_window.h gsc_settings.h gsc_text_window.h
gsmartcontrol_SOURCES = $(WIN_MAIN_SRC) gsc_about_dialog.cpp gsc_add_device_window.cpp \
gsc_executor_error_dialog.cpp gsc_executor_log_window.cpp gsc_info_window.cpp \
gsc_init.cpp gsc_main.cpp gsc_main_window.cpp gsc_preferences_window.cpp
gsmartcontrol_SOURCES = gsc_about_dialog.cpp gsc_executor_error_dialog.cpp \
gsc_executor_log_window.cpp gsc_help_window.cpp gsc_info_window.cpp gsc_init.cpp gsc_main.cpp \
gsc_main_window.cpp gsc_preferences_window.cpp
+8 -10
View File
@@ -2,16 +2,14 @@ AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
noinst_LIBRARIES = libapplib.a
libapplib_a_SOURCES = app_gtkmm_utils.cpp app_pango_utils.cpp cmdex.cpp \
cmdex_sync.cpp cmdex_sync_gui.cpp executor_factory.cpp gui_utils.cpp selftest.cpp smartctl_executor.cpp \
smartctl_parser.cpp storage_detector.cpp storage_detector_linux.cpp storage_detector_other.cpp \
storage_detector_win32.cpp storage_device.cpp storage_property.cpp \
cmdex_sync.cpp cmdex_sync_gui.cpp gui_utils.cpp selftest.cpp smartctl_executor.cpp \
smartctl_parser.cpp storage_detector.cpp storage_device.cpp storage_property.cpp \
storage_property_descr.cpp
noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pango_utils.h \
app_pcrecpp.h app_ui_res_utils.h cli_executors.h cmdex.h cmdex_sync.h cmdex_sync_gui.h executor_factory.h gui_utils.h \
app_pcrecpp.h app_ui_res_utils.h cmdex.h cmdex_sync.h cmdex_sync_gui.h gui_utils.h \
selftest.h smartctl_executor.h smartctl_executor_gui.h smartctl_parser.h \
storage_detector.h storage_detector_helpers.h storage_detector_linux.h storage_detector_other.h \
storage_detector_win32.h storage_device.h storage_property.h storage_property_colors.h \
storage_property_descr.h storage_settings.h
storage_detector.h storage_device.h storage_property.h storage_property_colors.h \
storage_property_descr.h storage_settings.h wrapping_label.h
# don't use absolute path for the current dir's .a, because the make
# dependency resolver won't get it (needed for parallel builds)
@@ -26,16 +24,16 @@ smartctl_executor_test_LDADD = libapplib.a \
smartctl_parser_test_SOURCES = smartctl_parser_test.cpp
smartctl_parser_test_LDADD = libapplib.a \
$(top_builddir)/src/libdebug/libdebug.a
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
storage_detector_test_SOURCES = storage_detector_test.cpp
storage_detector_test_LDADD = libapplib.a \
$(top_builddir)/src/libdebug/libdebug.a
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
spawn_test_SOURCES = spawn_test.cpp
# Build them first
SUBDIRS=../libdebug
SUBDIRS=../libdebug ../pcrecpp
# we don't list them in a separate variable because otherwise kdevelop won't see them.
+18 -18
View File
@@ -1,22 +1,16 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_GTKMM_FEATURES_H
#define APP_GTKMM_FEATURES_H
#include <gtkmm.h>
#include <gtkmmconfig.h>
/// \def APP_GTKMM_CHECK_VERSION(major, minor, micro)
/// Similar to GTK_CHECK_VERSION, but for gtkmm, which lacks this for some reason.
// Similar to GTK_CHECK_VERSION, but for gtkmm (this should have been in it)
#ifndef APP_GTKMM_CHECK_VERSION
#define APP_GTKMM_CHECK_VERSION(major, minor, micro) \
(GTKMM_MAJOR_VERSION > (major) \
@@ -29,6 +23,13 @@
// New tooltip API is available since 2.12
#if !APP_GTKMM_CHECK_VERSION(2, 12, 0)
#define APP_GTKMM_OLD_TOOLTIPS
#endif
// If default virtual on_* members are disabled in gtkmm, use this in constructors
// to connect on_signal_name methods.
@@ -50,13 +51,14 @@
*/
/// Connect to a signal _before_ the default handler. That is, if you want
/// to have, say, on_delete_event() in your window-inherited class, define
/// on_delete_event_before() instead and return true (handled) from it
/// if it's X event handler.
/// This method will work regardless of presence of default virtual handlers
/// in parent class. It will also avoid calling the handler twice (one from
/// signal, another from default virtual handler).
// Connect to a signal _before_ the default handler. That is, if you want
// to have, say, on_delete_event() in your window-inherited class, define
// on_delete_event_before() instead and return true (handled) from it
// if it's X event handler.
// This method will work regardless of presence of default virtual handlers
// in parent class. It will also avoid calling the handler twice (one from
// signal, another from default virtual handler).
#define APP_GTKMM_CONNECT_VIRTUAL(signal_name) \
this->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &self_type::on_ ## signal_name ## _before), false)
@@ -66,5 +68,3 @@
#endif
/// @}
+121 -66
View File
@@ -1,20 +1,19 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <gtkmm.h>
#include <glibmm.h>
#include <glib.h>
#include <cstring> // std::strlen
#include <gtk/gtk.h> // gtk_* stuff
#include <vector>
#include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS
#ifdef APP_GTKMM_OLD_TOOLTIPS
#include <gtk/gtk.h> // gtk_tooltips_*
#endif
#include "app_pango_utils.h" // app_pango_strip_markup()
#include "app_gtkmm_utils.h"
@@ -54,98 +53,156 @@ Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column)
void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
const Glib::ustring& tooltip_text, bool use_markup)
{
// set_tooltip_* is available since 2.12
#ifndef APP_GTKMM_OLD_TOOLTIPS
if (use_markup) {
widget.set_tooltip_markup(tooltip_text);
} else {
widget.set_tooltip_text(tooltip_text);
}
#else // use the old tooltips api
Gtk::Widget* toplevel = widget.get_toplevel();
if (toplevel && toplevel->is_toplevel()) { // orphan widgets return themselves, so check toplevelness.
GtkTooltips* tooltips = static_cast<GtkTooltips*>(toplevel->get_data("window_tooltips"));
if (tooltips) {
if (use_markup) {
// strip markup
Glib::ustring stripped;
if (app_pango_strip_markup(tooltip_text, stripped)) {
gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped.c_str(), "");
}
} else {
gtk_tooltips_set_tip(tooltips, widget.gobj(), tooltip_text.c_str(), "");
}
}
}
#endif
}
// unset model on treeview, cross-gtkmm-version.
void app_gtkmm_treeview_unset_model(Gtk::TreeView* treeview)
{
// gtkmm's TreeView::unset_model() is since gtkmm 2.8
// (and there's no version info in docs), so use this instead.
if (treeview)
gtk_tree_view_set_model(treeview->gobj(), 0);
}
// unset model on combobox, cross-gtkmm-version.
void app_gtkmm_combobox_unset_model(Gtk::ComboBox* box)
{
// there's no ComboBox::unset_model() in gtkmm (huh?)
if (box)
gtk_combo_box_set_model(box->gobj(), 0);
}
#ifdef APP_GTKMM_OLD_TOOLTIPS
namespace {
/// This has been copied from _g_utf8_make_valid() (glib-2.20.4).
/// _g_utf8_make_valid() is GLib's private function for auto-correcting
/// the potentially invalid utf-8 data.
inline gchar* gsc_g_utf8_make_valid (const gchar* name)
// helper function for gtkmm_set_treeview_tooltip_column()
inline bool app_on_treeview_motion_notify_event_tooltip(GdkEventMotion* event, Gtk::TreeView* treeview,
Gtk::TreeModelColumn<Glib::ustring> tooltip_column)
{
GString* str;
const gchar* remainder, *invalid;
gint remaining_bytes, valid_bytes;
// debug_out_dump("app", DBG_FUNC_MSG << "x=" << int(event->x) << ", y=" << int(event->y) << "\n");
g_return_val_if_fail (name != NULL, NULL);
Gtk::TreePath path;
Gtk::TreeViewColumn* mouse_column = 0;
int cell_x = 0, cell_y = 0;
str = NULL;
remainder = name;
remaining_bytes = gint(std::strlen(name));
while (remaining_bytes != 0) {
if (g_utf8_validate (remainder, remaining_bytes, &invalid))
break;
valid_bytes = gint(invalid - remainder);
if (str == NULL)
str = g_string_sized_new (remaining_bytes);
g_string_append_len (str, remainder, valid_bytes);
/* append U+FFFD REPLACEMENT CHARACTER */
g_string_append (str, "\357\277\275");
remaining_bytes -= valid_bytes + 1;
remainder = invalid + 1;
if (treeview->get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y),
path, mouse_column, cell_x, cell_y)) { // returns true if we're on a cell
app_gtkmm_set_widget_tooltip(*treeview, (*(treeview->get_model()->get_iter(path)))[tooltip_column], true);
}
if (str == NULL)
return g_strdup (name);
return false; // continue handling (no conflicts there?)
}
g_string_append (str, remainder);
g_assert (g_utf8_validate (str->str, -1, NULL));
// helper function for gtkmm_set_iconview_tooltip_column()
inline bool app_on_iconview_motion_notify_event_tooltip(GdkEventMotion* event, Gtk::IconView* iconview,
Gtk::TreeModelColumn<Glib::ustring> tooltip_column, Glib::RefPtr<Gtk::ListStore> model)
{
// debug_out_dump("app", DBG_FUNC_MSG << "x=" << int(event->x) << ", y=" << int(event->y) << "\n");
return g_string_free (str, FALSE);
// don't use get_item_at_pos() - it's not available in gtkmm < 2.8.
Gtk::TreePath tpath = iconview->get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y));
if (tpath.gobj() && !tpath.empty()) { // without gobj() check gtkmm 2.6 (but not 2.12) prints lots of errors
// somehow, getting model from iconview doesn't work (segfault).
// Glib::RefPtr<Gtk::TreeModel> model = iconview->get_model();
if (tpath.gobj())
app_gtkmm_set_widget_tooltip(*iconview, (*(model->get_iter(tpath)))[tooltip_column], true);
}
return false; // continue handling (no conflicts there?)
}
}
#endif
Glib::ustring app_ustring_from_gchar(gchar* str)
void gtkmm_set_treeview_tooltip_column(Gtk::TreeView* treeview,
Gtk::TreeModelColumn<Glib::ustring>& col_tooltip)
{
if (!str) {
return Glib::ustring();
}
Glib::ustring ustr(str);
g_free(str);
return ustr;
if (!treeview)
return;
#ifndef APP_GTKMM_OLD_TOOLTIPS
treeview->set_tooltip_column(col_tooltip.index()); // gtkmm has only index-based api here
#else // manually set tooltips when hovering over cells
treeview->signal_motion_notify_event().connect( // connect before other events, or we'll get some screwed event data
sigc::bind(sigc::bind(sigc::ptr_fun(&app_on_treeview_motion_notify_event_tooltip), col_tooltip), treeview), false);
#endif
}
Glib::ustring app_utf8_make_valid(const Glib::ustring& str)
void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview,
Gtk::TreeModelColumn<Glib::ustring>& col_tooltip, Glib::RefPtr<Gtk::ListStore> model)
{
char* s = gsc_g_utf8_make_valid(str.c_str());
if (!s) {
return Glib::ustring();
}
Glib::ustring res(s);
g_free(s);
return res;
if (!iconview)
return;
#ifndef APP_GTKMM_OLD_TOOLTIPS
iconview->set_tooltip_column(col_tooltip.index()); // gtkmm has only index-based api here
#else // manually set tooltips when hovering over cells
iconview->signal_motion_notify_event().connect( // connect before other events, or we'll get some screwed event data
sigc::bind(sigc::bind(sigc::bind(sigc::ptr_fun(&app_on_iconview_motion_notify_event_tooltip), model), col_tooltip), iconview), false);
#endif
}
Glib::ustring app_output_make_valid(const Glib::ustring& str)
bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr<Gtk::IconTheme> theme,
const Glib::ustring& icon_name, int size)
{
#ifdef _WIN32
try {
return app_utf8_make_valid(Glib::locale_to_utf8(str));
} catch (Glib::ConvertError& e) {
// nothing, try to fix as it is
if (!theme || !theme->has_icon(icon_name)) // check if it exists first, or exception may be thrown.
return false;
std::vector<int> sizes = theme->get_icon_sizes(icon_name);
for (std::vector<int>::const_iterator iter = sizes.begin(); iter != sizes.end(); ++iter) {
if (*iter == size || *iter == -1) // -1 means scalable
return true;
}
#endif
return app_utf8_make_valid(str);
return false;
}
@@ -153,5 +210,3 @@ Glib::ustring app_output_make_valid(const Glib::ustring& str)
/// @}
+55 -49
View File
@@ -1,91 +1,97 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_GTKMM_UTILS_H
#define APP_GTKMM_UTILS_H
#include <string>
#include <gtkmm.h>
#include <gtkmm/widget.h>
#include <gtkmm/treeviewcolumn.h>
#include <gtkmm/treemodelcolumn.h>
#include <gtkmm/treeview.h>
#include <gtkmm/combobox.h>
#include <gtkmm/cellrenderertext.h>
#include <gtkmm/iconview.h>
#include <gtkmm/liststore.h>
#include <gtkmm/icontheme.h> // must include (even if gtkmm.h is included) for older gtkmm (at least 2.6, but not 2.12)
#include "hz/down_cast.h"
/// Get column header widget of a tree view column.
/// Note: This works only if the column has custom widget set.
// Note: This works only if the column has custom widget set.
Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column);
/// Read column header text and create a label with that text. Set the label as
/// column's custom widget and return it.
// Read column header text and create a label with that text, set it as column's custom widget.
Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column);
/// A wrapper around set_tooltip_*() for portability across different gtkmm versions.
// unset model on treeview, cross-gtkmm-version.
void app_gtkmm_treeview_unset_model(Gtk::TreeView* treeview);
// unset model on combobox (there's no straight way to do it in gtkmm, I think)
void app_gtkmm_combobox_unset_model(Gtk::ComboBox* box);
// A wrapper around set_tooltip_*() for portability across different gtkmm versions.
void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
const Glib::ustring& tooltip_text, bool use_markup = false);
// A portable wrapper around TreeView::set_tooltip_column().
void gtkmm_set_treeview_tooltip_column(Gtk::TreeView* treeview,
Gtk::TreeModelColumn<Glib::ustring>& col_tooltip);
/// Convenience function for creating a TreeViewColumn .
template<typename T>
// A portable wrapper around IconView::set_tooltip_column().
void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview,
Gtk::TreeModelColumn<Glib::ustring>& col_tooltip, Glib::RefPtr<Gtk::ListStore> model);
// Convenience function for creating a TreeViewColumn .
template<typename T> inline
int app_gtkmm_create_tree_view_column(Gtk::TreeModelColumn<T>& mcol, Gtk::TreeView& treeview,
const Glib::ustring& title, const Glib::ustring& tooltip_text, bool sortable = false, bool cell_markup = false)
{
int num_tree_cols = treeview.append_column(title, mcol);
Gtk::TreeViewColumn* tcol = treeview.get_column(num_tree_cols - 1);
if (tcol) {
if (sortable)
tcol->set_sort_column(mcol);
int num_tree_cols = treeview.append_column(title, mcol);
Gtk::TreeViewColumn* tcol = treeview.get_column(num_tree_cols - 1);
if (tcol) {
if (sortable)
tcol->set_sort_column(mcol);
app_gtkmm_labelize_column(*tcol);
tcol->set_reorderable(true);
tcol->set_resizable(true);
}
Gtk::Widget* header = app_gtkmm_get_column_header(*tcol);
if (header)
app_gtkmm_set_widget_tooltip(*header, tooltip_text);
if (cell_markup) {
Gtk::CellRendererText* cr_type = hz::down_cast<Gtk::CellRendererText*>(treeview.get_column_cell_renderer(num_tree_cols - 1));
if (cr_type) { // may not be true if it's not Text (unless static_cast is used, in which case we're screwed)
treeview.get_column(num_tree_cols - 1)->clear_attributes(*cr_type); // clear "text" attribute. "markup" won't work without this.
treeview.get_column(num_tree_cols - 1)->add_attribute(cr_type->property_markup(), mcol); // render col_type as markup.
app_gtkmm_labelize_column(*tcol);
tcol->set_reorderable(true);
tcol->set_resizable(true);
}
}
return num_tree_cols;
Gtk::Widget* header = app_gtkmm_get_column_header(*tcol);
if (header)
app_gtkmm_set_widget_tooltip(*header, tooltip_text);
if (cell_markup) {
Gtk::CellRendererText* cr_type = hz::down_cast<Gtk::CellRendererText*>(treeview.get_column_cell_renderer(num_tree_cols - 1));
if (cr_type) { // may not be true if it's not Text (unless static_cast is used, in which case we're screwed)
treeview.get_column(num_tree_cols - 1)->clear_attributes(*cr_type); // clear "text" attribute. "markup" won't work without this.
treeview.get_column(num_tree_cols - 1)->add_attribute(cr_type->property_markup(), mcol); // render col_type as markup.
}
}
return num_tree_cols;
}
/// Get Glib::ustring from gchar*, freeing gchar*.
Glib::ustring app_ustring_from_gchar(gchar* str);
bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr<Gtk::IconTheme> theme,
const Glib::ustring& icon_name, int size);
/// Convert a possibly invalid utf-8 string to valid utf-8.
/// \param str string to test and fix.
Glib::ustring app_utf8_make_valid(const Glib::ustring& str);
/// Make command output a valid utf-8 string. Essentially, this calls app_utf8_make_valid(),
/// supplying true for \c in_locale under Win32, and false under other systems.
/// The reason for this is that in Win32 we can't execute commands under C locale,
/// but we do execute them under C in other systems.
Glib::ustring app_output_make_valid(const Glib::ustring& str);
#endif
/// @}
+1 -8
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <pango/pango.h> // pango_parse_markup()
@@ -53,5 +48,3 @@ bool app_pango_strip_markup(const std::string& str, std::string& stripped)
/// @}
+2 -12
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_PANGO_UTILS_H
#define APP_PANGO_UTILS_H
@@ -17,13 +12,10 @@
/// Strip a string of all markup tags.
/// \return false if there was some error.
// strip markup of a string. returns true on success.
bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped);
/// Strip a string of all markup tags.
/// \return false if there was some error.
bool app_pango_strip_markup(const std::string& str, std::string& stripped);
@@ -31,5 +23,3 @@ bool app_pango_strip_markup(const std::string& str, std::string& stripped);
#endif
/// @}
+30 -48
View File
@@ -1,37 +1,29 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_PCRECPP_H
#define APP_PCRECPP_H
// A wrapper header for pcrecpp
// A wrapper header for bundled pcrecpp
// disable all that declspec BS
#ifndef PCRE_STATIC
# define PCRE_STATIC
#endif
#include "pcrecpp/pcrecpp_internal.h"
#include "pcrecpp/pcrecpp.h"
#include <pcrecpp.h>
#include <string>
#include "hz/debug.h"
/// Take a string of characters where each character represents a modifier
/// and return the appropriate pcre options.
/// - i - case insensitive match.
/// - m - multiline, read past the first line.
/// - s - dot matches newlines.
/// - E - $ matches only the end of the string (D in php, not available in perl).
/// - X - strict escape parsing (not available in perl).
/// - x - ignore whitespaces.
/// - 8 - handles UTF8 characters in pattern (u in php, not available in perl).
/// - U - ungreedy, reverses * and *? (not available in perl).
/// - N - disables matching parentheses (not available in perl or php).
inline pcrecpp::RE_Options app_pcre_get_options(const char* modifiers)
{
// ANYCRLF means any of crlf, cr, lf. Used in ^ and $.
@@ -67,12 +59,13 @@ inline pcrecpp::RE_Options app_pcre_get_options(const char* modifiers)
/// Accept pattern in form of "/pattern/modifiers".
/// Note: Slashes should be escaped within the pattern.
/// If the string doesn't start with a slash, it is treated as an ordinary pattern
/// without modifiers.
/// This function will make a RE object with ANYCRLF option set for portability
/// across various pcre builds.
// Accept pattern in form of "/pattern/modifiers".
// Note: Slashes should be escaped within the pattern.
// If the string doesn't start with a slash, it is treated as an ordinary pattern
// without modifiers.
// This function will make a RE object with ANYCRLF option set for portability
// across various pcre builds.
inline pcrecpp::RE app_pcre_re(const std::string& perl_pattern)
{
if (perl_pattern.size() >= 2 && perl_pattern[0] == '/') {
@@ -91,7 +84,7 @@ inline pcrecpp::RE app_pcre_re(const std::string& perl_pattern)
/// Match a string against a pattern in "/pattern/modifiers" format.
inline bool app_pcre_match(const std::string& perl_pattern, const std::string& str,
const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg,
const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg,
@@ -115,9 +108,7 @@ inline bool app_pcre_match(const std::string& perl_pattern, const std::string& s
}
/// Match a string against a pattern in "/pattern/modifiers" format.
/// This overload is needed to avoid confusion with RE.
// This is needed to avoid confusion with RE.
inline bool app_pcre_match(const char* perl_pattern, const std::string& str,
const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg,
const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg,
@@ -141,8 +132,6 @@ inline bool app_pcre_match(const char* perl_pattern, const std::string& str,
}
/// Match a string against a pattern in "/pattern/modifiers" format.
inline bool app_pcre_match(const pcrecpp::RE& re, const std::string& str,
const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg,
const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg,
@@ -167,23 +156,22 @@ inline bool app_pcre_match(const pcrecpp::RE& re, const std::string& str,
/// Replace every occurence of pattern with replacement string in \c subject.
/// The pattern is in "/pattern/modifiers" format.
// Replaces every occurence of pattern with replacement in subject.
inline int app_pcre_replace(const std::string& perl_pattern, const std::string& replacement, std::string& subject)
{
return app_pcre_re(perl_pattern).GlobalReplace(replacement, &subject);
}
/// Replace every occurence of pattern with replacement string in \c subject.
/// The pattern is in "/pattern/modifiers" format.
// Replaces every occurence of pattern with replacement in subject.
inline int app_pcre_replace(const char* perl_pattern, const std::string& replacement, std::string& subject)
{
return app_pcre_replace(std::string(perl_pattern), replacement, subject);
}
/// Replace every occurence of pattern with replacement string in \c subject.
// Replaces every occurence of pattern with replacement in subject.
inline int app_pcre_replace(const pcrecpp::RE& re, const std::string& replacement, std::string& subject)
{
return re.GlobalReplace(replacement, &subject);
@@ -191,25 +179,22 @@ inline int app_pcre_replace(const pcrecpp::RE& re, const std::string& replacemen
/// Replace the first occurence of pattern with replacement string in \c subject.
/// The pattern is in "/pattern/modifiers" format.
// Replaces first occurence of pattern with replacement in subject.
inline bool app_pcre_replace_once(const std::string& perl_pattern, const std::string& replacement, std::string& subject)
{
return app_pcre_re(perl_pattern).Replace(replacement, &subject);
}
/// Replace the first occurence of pattern with replacement string in \c subject.
/// The pattern is in "/pattern/modifiers" format.
// Replaces first occurence of pattern with replacement in subject.
inline bool app_pcre_replace_once(const char* perl_pattern, const std::string& replacement, std::string& subject)
{
return app_pcre_replace_once(std::string(perl_pattern), replacement, subject);
}
/// Replace first occurence of pattern with replacement string in \c subject.
// Replaces first occurence of pattern with replacement in subject.
inline bool app_pcre_replace_once(const pcrecpp::RE& re, const std::string& replacement, std::string& subject)
{
return re.Replace(replacement, &subject);
@@ -217,8 +202,7 @@ inline bool app_pcre_replace_once(const pcrecpp::RE& re, const std::string& repl
/// Escape a string to be used inside a regular expression. The result
/// won't contain any special expression characters.
// Escape a string to be used as a regular expression
inline std::string app_pcre_escape(const std::string& str)
{
return pcrecpp::RE::QuoteMeta(str);
@@ -229,5 +213,3 @@ inline std::string app_pcre_escape(const std::string& str)
#endif
/// @}
+220 -86
View File
@@ -1,28 +1,33 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_UI_RES_UTILS_H
#define APP_UI_RES_UTILS_H
#include <string>
#include <gtkmm.h>
#include <gtkmm/window.h>
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
// Old glibmm versions had exceptions but didn't define this at all.
// New ones define it to 0 if there are no glibmm exceptions.
#if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED
#if defined ENABLE_LIBGLADE
#include <libglademm.h>
#elif defined ENABLE_GTKBUILDER
#include <gtkmm/builder.h>
#endif
#if GLIBMM_EXCEPTIONS_ENABLED
#include <memory> // std::auto_ptr
#endif
#include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS
#ifdef APP_GTKMM_OLD_TOOLTIPS
#include <gtk/gtk.h> // gtk_tooltips_*
#endif
#include "hz/debug.h"
#include "hz/instance_manager.h"
#include "hz/down_cast.h"
@@ -32,68 +37,161 @@
/// \def APP_UI_RES_DATA_INIT(res_name)
/// Use these in window class definitions to declare ui resources.
/// E.g. APP_UI_RES_DATA_INIT(main_window) will search for
/// main_window.ui in data file search paths.
/// Or, if you're using compiled-in buffers, it will make them available.
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".ui", UIResDataBase); \
struct UIResData : public UIResDataBase { \
UIResData() \
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
}
// Use these in window class definitions to declare ui resources.
// E.g. HZ_RES_DATA_INIT(main_window) will search for
// main_window.glade or main_window.ui (depending on whether
// you're using libglade or gtkbuilder) in data file search paths.
// Or, if you're using compiled-in buffers, it will make them available.
#if defined ENABLE_LIBGLADE
/// \typedef app_ui_res_ref_t
/// A reference-counting pointer to application UI resource
typedef Glib::RefPtr<Gtk::Builder> app_ui_res_ref_t;
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".glade", UIResDataBase); \
struct UIResData : public UIResDataBase { \
UIResData() \
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
}
/// \fn bool app_ui_res_create_from(app_ui_res_ref_t& ref, const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
/// Create application UI resource from a static buffer.
inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
{
if (!buf_size || !buf || !buf[0]) {
error_msg = "Cannot load data buffers.";
return false;
}
#elif defined ENABLE_GTKBUILDER
#if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED
try {
// ref->add_from_file("main_window.ui");
ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size));
}
catch (Gtk::BuilderError& ex) { // the docs say Glib::MarkupError, but examples say otherwise.
error_msg = ex.what();
return false;
}
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".ui", UIResDataBase); \
struct UIResData : public UIResDataBase { \
UIResData() \
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
}
#else
std::auto_ptr<Glib::BuilderError> error;
ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size),"", "", error);
#else // none, just avoid errors
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_DUMMY_INIT_NAMED(res_name##_ui, 0, UIResDataBase); \
struct UIResData : public UIResDataBase { \
UIResData() \
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
}
if (error.get()) {
error_msg = error->what();
return false;
}
#endif
return true;
}
#if defined ENABLE_LIBGLADE
typedef Glib::RefPtr<Gnome::Glade::Xml> app_ui_res_ref_t;
inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
{
if (!buf_size || !buf || !buf[0]) {
error_msg = "Cannot load data buffers.";
return false;
}
#if GLIBMM_EXCEPTIONS_ENABLED
try {
// Glib::RefPtr<Gnome::Glade::Xml> ref = Gnome::Glade::Xml::create("main_window.glade");
ref = Gnome::Glade::Xml::create_from_buffer(reinterpret_cast<const char*>(buf),
static_cast<int>(buf_size));
}
catch (Gnome::Glade::XmlError& ex) {
error_msg = ex.what();
return false;
}
#else
std::auto_ptr<Gnome::Glade::XmlError> error;
ref = Gnome::Glade::Xml::create_from_buffer(reinterpret_cast<const char*>(buf),
static_cast<int>(buf_size),"", "", error);
if (error.get()) {
error_msg = error->what();
return false;
}
#endif
return true;
}
#elif defined ENABLE_GTKBUILDER
typedef Glib::RefPtr<Gtk::Builder> app_ui_res_ref_t;
inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
{
if (!buf_size || !buf || !buf[0]) {
error_msg = "Cannot load data buffers.";
return false;
}
#if GLIBMM_EXCEPTIONS_ENABLED
try {
// ref->add_from_file("main_window.ui");
ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size));
}
catch (Gtk::BuilderError& ex) { // the docs say Glib::MarkupError, but examples say otherwise.
error_msg = ex.what();
return false;
}
#else
std::auto_ptr<Glib::BuilderError> error;
ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size),"", "", error);
if (error.get()) {
error_msg = error->what();
return false;
}
#endif
return true;
}
#else // none, just avoid compilation errors
struct AppUIResDummy {
template<typename T>
void get_widget_derived(const Glib::ustring& name, T*& widget)
{ }
template<typename T>
void get_widget(const Glib::ustring& name, T*& widget)
{ }
};
typedef AppUIResDummy* app_ui_res_ref_t;
inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
{
error_msg = "Neither Gtk::Builder (gtkmm >= 2.12) nor libglademm were present during compilation."
" Recompile when either dependency is resolved.";
return false;
}
#endif
// These allow easy attaching of gtkbuilder widget signals to member functions
// These allow easy attaching of glade widget signals to member functions
/// Connect member function (callback) to signal \c signal_name on widget
/// \c ui_element, where \c ui_element is the widget's gtkbuilder name.
// Connect member function (callback) to signal signal_name on widget
// widget_name, where widget_name is the widget's glade/gtkbuilder name.
#define APP_UI_RES_CONNECT(ui_element, signal_name, callback) \
if (true) { \
if (!ui_element) \
@@ -104,9 +202,7 @@ inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
} else (void)0
/// Connect member function (callback) with a name of \c on_<widget_name>_<signal_name>
/// to signal \c signal_name on widget \c ui_element, where \c ui_element is the
/// widget's gtkbuilder name.
// Same as above, but default callback name to on_<widget_name>_<signal_name>.
#define APP_UI_RES_AUTO_CONNECT(ui_element, signal_name) \
APP_UI_RES_CONNECT(ui_element, signal_name, on_ ## ui_element ## _ ## signal_name)
@@ -114,29 +210,38 @@ inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
/// Inherit this when using GtkBuilder-enabled windows (or any other GtkBuilder-enabled objects).
/// \c Child is the child class that inherits all the functionality of having instance lifetime
/// management and other benefits.
/// If \c MultiInstance is false, create() will return the same instance each time.
// Inherit from this when using Glade-enabled windows (or any other objects).
template<class Child, bool MultiInstance, class WidgetType = Gtk::Window>
class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, MultiInstance> {
public:
/// Instance class type, which is also the parent class.
typedef hz::InstanceManager<Child, MultiInstance> instance_class;
#if defined ENABLE_LIBGLADE
friend class Gnome::Glade::Xml; // allow construction through libglade
#elif defined ENABLE_GTKBUILDER
friend class Gtk::Builder; // allow construction through gtkbuilder
#endif
friend class hz::InstanceManager<Child, MultiInstance>; // allow construction through instance class
/// Override parent hz::InstanceManager's function because of non-trivial constructor
// overload parent's function because of non-trivial constructor
static Child* create()
{
if (hz::InstanceManager<Child, MultiInstance>::has_single_instance()) // for single-instance objects
return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
std::string error;
#if defined ENABLE_LIBGLADE
app_ui_res_ref_t ui;
#elif defined ENABLE_GTKBUILDER
app_ui_res_ref_t ui = Gtk::Builder::create();
const typename Child::UIResData data; // this holds the GtkBuilder data
#else // none, just avoid compilation errors
app_ui_res_ref_t ui = 0;
#endif
const typename Child::UIResData data; // this holds the glade data
// this does the actual object construction
bool success = app_ui_res_create_from(ui, data.buf, data.size, error);
@@ -166,21 +271,19 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
}
/// Get UI resource
app_ui_res_ref_t get_ui()
{
return ref_ui_;
}
/// Find a widget in UI and return it.
Gtk::Widget* lookup_widget(const Glib::ustring& name)
{
return lookup_widget<Gtk::Widget*>(name);
}
/// Find a widget in UI and return it.
template<typename WidgetPtr>
WidgetPtr lookup_widget(const Glib::ustring& name)
{
@@ -188,8 +291,6 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
return lookup_widget(name, w);
}
/// Find a widget in UI and return it.
template<typename WidgetPtr>
WidgetPtr lookup_widget(const Glib::ustring& name, WidgetPtr& w)
{
@@ -198,14 +299,17 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
}
/// Find an object in UI and return it.
Glib::Object* lookup_object(const Glib::ustring& name)
{
#if defined ENABLE_LIBGLADE
return static_cast<Glib::Object*>(lookup_widget(name)); // all glade objects are widgets, it's an upcast
#elif defined ENABLE_GTKBUILDER
return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get().
#else // none, just avoid compilation errors
return 0;
#endif
}
/// Find an object in UI and return it.
template<typename ObjectPtr>
ObjectPtr lookup_object(const Glib::ustring& name)
{
@@ -213,8 +317,6 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
return lookup_object(name, obj);
}
/// Find an object in UI and return it.
template<typename ObjectPtr>
ObjectPtr lookup_object(const Glib::ustring& name, ObjectPtr& obj)
{
@@ -225,21 +327,28 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
protected:
typedef Child self_type; ///< This is needed by APP_UI_ macros
typedef WidgetType widget_type; ///< This is needed by APP_UI_ macros
// This is needed by APP_GLADE_ macros
typedef Child self_type;
// protected constructor / destructor, use create() / destroy() instead of new / delete.
/// GtkBuilder needs this constructor in a child.
/// BaseObjectType is a C type, defined in specific Gtk:: widget class.
// glade needs this constructor in a child.
// BaseObjectType is a C type, defined in specific Gtk:: widget class.
AppUIResWidget(typename WidgetType::BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
: WidgetType(gtkcobj), ref_ui_(ref_ui)
{
// if gtkmm is older than 2.12, use the old tooltips API
#ifdef APP_GTKMM_OLD_TOOLTIPS
// attach it to the window
this->create_tooltips_data(this);
#endif
// manually connecting signals:
// this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete));
// signals of GtkBuilder-created objects:
// signals of glade-created objects:
// Gtk::ToolButton* rescan_devices_toolbutton = 0;
// APP_UI_RES_AUTO_CONNECT(rescan_devices_toolbutton, clicked);
@@ -247,14 +356,39 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
}
/// Virtual destructor
virtual ~AppUIResWidget()
{ }
#ifdef APP_GTKMM_OLD_TOOLTIPS
// differentiate Window and its children from other widgets through overloading
void create_tooltips_data(Gtk::Window* window)
{
if (window) {
// gtkmm tooltips api is somewhat broken (no create?), so use gtk
if (!window->get_data("window_tooltips")) {
window->set_data("window_tooltips", gtk_tooltips_new());
}
}
}
void create_tooltips_data(Gtk::Object* o)
{ }
#endif
protected:
// void on_rescan_devices_toolbutton_clicked()
// {
// }
private:
app_ui_res_ref_t ref_ui_; ///< UI resource
app_ui_res_ref_t ref_ui_;
};
@@ -263,6 +397,6 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
#endif
/// @}
#endif
+13 -24
View File
@@ -1,17 +1,16 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib_tests
/// \weakgroup applib_tests
/// @{
#include <iostream>
#include <gtkmm.h>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
#include <gtkmm/main.h>
#include "app_ui_res_utils.h"
// #include "hz/instance_manager.h"
@@ -20,27 +19,25 @@
class AppUiResTestWindow;
/// Window for the resource utilities test
// class AppUiResTestWindow : public Gtk::Window
// class AppUiResTestWindow : public hz::InstanceManager<AppUiResTestWindow, false>, public Gtk::Window
class AppUiResTestWindow : public AppUIResWidget<AppUiResTestWindow, false>
{
public:
// name of ui file without a .ui extension and quotes
// name of glade/ui file without a .glade/.ui extension and quotes
APP_UI_RES_DATA_INIT(app_ui_res_test_window);
/// Action types
enum action_t {
action_quit ///< Quit action
action_quit
};
/// Constructor
// AppUiResTestWindow()
AppUiResTestWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
: AppUIResWidget<AppUiResTestWindow, false>(gtkcobj, ref_ui)
{
Gtk::Box* vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5));
Gtk::Box* vbox = Gtk::manage(new Gtk::VBox(false, 5));
add(*vbox);
Gtk::Button* button = Gtk::manage(new Gtk::Button("Clicky"));
@@ -57,27 +54,22 @@ class AppUiResTestWindow : public AppUIResWidget<AppUiResTestWindow, false>
show_all();
}
/// Virtual destructor
virtual ~AppUiResTestWindow()
{ }
private:
/// Action callback
void on_button_clicked()
{
std::cerr << "AppUiResTestWindow::on_button_clicked()\n";
}
/// Action callback
void on_button_clicked2(action_t action_type)
{
std::cerr << "AppUiResTestWindow::on_button_clicked2()\n";
}
/// Action callback
bool on_delete_event(GdkEventAny* e)
{
Gtk::Main::quit();
@@ -88,7 +80,6 @@ class AppUiResTestWindow : public AppUIResWidget<AppUiResTestWindow, false>
/// Main function of the test
int main(int argc, char *argv[])
{
Gtk::Main m(&argc, &argv);
@@ -106,5 +97,3 @@ int main(int argc, char *argv[])
/// @}
+342 -2
View File
@@ -1,12 +1,348 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
// A simple empty window definition
#if defined ENABLE_LIBGLADE
extern const unsigned char app_ui_res_test_window_ui[] = {
0x3C,
0x3F,
0x78,
0x6D,
0x6C,
0x20,
0x76,
0x65,
0x72,
0x73,
0x69,
0x6F,
0x6E,
0x3D,
0x22,
0x31,
0x2E,
0x30,
0x22,
0x20,
0x65,
0x6E,
0x63,
0x6F,
0x64,
0x69,
0x6E,
0x67,
0x3D,
0x22,
0x55,
0x54,
0x46,
0x2D,
0x38,
0x22,
0x20,
0x73,
0x74,
0x61,
0x6E,
0x64,
0x61,
0x6C,
0x6F,
0x6E,
0x65,
0x3D,
0x22,
0x6E,
0x6F,
0x22,
0x3F,
0x3E,
0xA,
0x3C,
0x21,
0x44,
0x4F,
0x43,
0x54,
0x59,
0x50,
0x45,
0x20,
0x67,
0x6C,
0x61,
0x64,
0x65,
0x2D,
0x69,
0x6E,
0x74,
0x65,
0x72,
0x66,
0x61,
0x63,
0x65,
0x20,
0x53,
0x59,
0x53,
0x54,
0x45,
0x4D,
0x20,
0x22,
0x67,
0x6C,
0x61,
0x64,
0x65,
0x2D,
0x32,
0x2E,
0x30,
0x2E,
0x64,
0x74,
0x64,
0x22,
0x3E,
0xA,
0x3C,
0x67,
0x6C,
0x61,
0x64,
0x65,
0x2D,
0x69,
0x6E,
0x74,
0x65,
0x72,
0x66,
0x61,
0x63,
0x65,
0x3E,
0xA,
0x20,
0x20,
0x3C,
0x77,
0x69,
0x64,
0x67,
0x65,
0x74,
0x20,
0x63,
0x6C,
0x61,
0x73,
0x73,
0x3D,
0x22,
0x47,
0x74,
0x6B,
0x57,
0x69,
0x6E,
0x64,
0x6F,
0x77,
0x22,
0x20,
0x69,
0x64,
0x3D,
0x22,
0x61,
0x70,
0x70,
0x5F,
0x75,
0x69,
0x5F,
0x72,
0x65,
0x73,
0x5F,
0x74,
0x65,
0x73,
0x74,
0x5F,
0x77,
0x69,
0x6E,
0x64,
0x6F,
0x77,
0x22,
0x3E,
0xA,
0x20,
0x20,
0x20,
0x20,
0x3C,
0x70,
0x72,
0x6F,
0x70,
0x65,
0x72,
0x74,
0x79,
0x20,
0x6E,
0x61,
0x6D,
0x65,
0x3D,
0x22,
0x74,
0x69,
0x74,
0x6C,
0x65,
0x22,
0x20,
0x74,
0x72,
0x61,
0x6E,
0x73,
0x6C,
0x61,
0x74,
0x61,
0x62,
0x6C,
0x65,
0x3D,
0x22,
0x79,
0x65,
0x73,
0x22,
0x3E,
0x54,
0x65,
0x73,
0x74,
0x3C,
0x2F,
0x70,
0x72,
0x6F,
0x70,
0x65,
0x72,
0x74,
0x79,
0x3E,
0xA,
0x20,
0x20,
0x20,
0x20,
0x3C,
0x63,
0x68,
0x69,
0x6C,
0x64,
0x3E,
0xA,
0x20,
0x20,
0x20,
0x20,
0x20,
0x20,
0x3C,
0x70,
0x6C,
0x61,
0x63,
0x65,
0x68,
0x6F,
0x6C,
0x64,
0x65,
0x72,
0x2F,
0x3E,
0xA,
0x20,
0x20,
0x20,
0x20,
0x3C,
0x2F,
0x63,
0x68,
0x69,
0x6C,
0x64,
0x3E,
0xA,
0x20,
0x20,
0x3C,
0x2F,
0x77,
0x69,
0x64,
0x67,
0x65,
0x74,
0x3E,
0xA,
0x3C,
0x2F,
0x67,
0x6C,
0x61,
0x64,
0x65,
0x2D,
0x69,
0x6E,
0x74,
0x65,
0x72,
0x66,
0x61,
0x63,
0x65,
0x3E,
0xA,
0x0 };
extern const unsigned int app_ui_res_test_window_ui_size = 319;
#elif defined ENABLE_GTKBUILDER
extern const unsigned char app_ui_res_test_window_ui[] = {
@@ -240,3 +576,7 @@ extern const unsigned char app_ui_res_test_window_ui[] = {
extern const unsigned int app_ui_res_test_window_ui_size = 224;
#endif
-247
View File
@@ -1,247 +0,0 @@
/**************************************************************************
Copyright:
(C) 2011 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef CLI_EXECUTORS_H
#define CLI_EXECUTORS_H
#include "cmdex.h"
#include "cmdex_sync.h"
/// Executor for tw_cli (3ware utility)
template<class ExecutorSync>
class TwCliExecutorGeneric : public ExecutorSync {
public:
/// Constructor
TwCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs)
: ExecutorSync(cmd, cmdargs)
{
this->construct();
}
/// Constructor
TwCliExecutorGeneric()
{
this->construct();
}
/// Virtual destructor
virtual ~TwCliExecutorGeneric()
{ }
protected:
/// Called from constructors
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&TwCliExecutorGeneric::translate_exit_status, NULL);
this->set_error_header("An error occurred while executing tw_cli:\n\n");
}
/// Exit status translate handler
static std::string translate_exit_status(int status, void* user_data)
{
return std::string();
}
/// Import the last error from command executor and clear all errors there
virtual void import_error()
{
Cmdex& cmdex = this->get_command_executor();
cmdex.errors_lock();
Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones
hz::ErrorBase* e = 0;
// find the last relevant error.
// note: const_reverse_iterator doesn't work on gcc 3, so don't do it.
for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++iter) {
// ignore iochannel errors, they may mask the real errors
if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") {
e = (*iter)->clone();
break;
}
}
cmdex.clear_errors(false); // and clear them
cmdex.errors_unlock();
if (e) { // if error is present, alert the user
on_error_warn(e);
}
}
/// This is called when an error occurs in command executor.
/// Note: The warnings are already printed via debug_* in cmdex.
virtual void on_error_warn(hz::ErrorBase* e)
{
if (!e)
return;
// import the error only if it's relevant.
std::string error_type = e->get_type();
// ignore giochannel errors - higher level errors will be triggered, and they more user-friendly.
if (error_type == "giochannel" || error_type == "custom") {
return;
}
this->set_error_msg(e->get_message());
}
};
/// tw_cli executor without GUI support
typedef TwCliExecutorGeneric<CmdexSync> TwCliExecutor;
/// A reference-counting pointer to TwCliExecutor
typedef hz::intrusive_ptr<TwCliExecutor> TwCliExecutorRefPtr;
/// tw_cli executor with GUI support
typedef TwCliExecutorGeneric<CmdexSyncGui> TwCliExecutorGui;
/// A reference-counting pointer to TwCliExecutorGui
typedef hz::intrusive_ptr<TwCliExecutorGui> TwCliExecutorGuiRefPtr;
/// Executor for cli (Areca utility)
template<class ExecutorSync>
class ArecaCliExecutorGeneric : public ExecutorSync {
public:
/// Constructor
ArecaCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs)
: ExecutorSync(cmd, cmdargs)
{
this->construct();
}
/// Constructor
ArecaCliExecutorGeneric()
{
this->construct();
}
/// Virtual destructor
virtual ~ArecaCliExecutorGeneric()
{ }
protected:
/// Called from constructors
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&ArecaCliExecutorGeneric::translate_exit_status, NULL);
this->set_error_header("An error occurred while executing Areca cli:\n\n");
}
/// Exit status translate handler
static std::string translate_exit_status(int status, void* user_data)
{
return std::string();
}
/// Import the last error from command executor and clear all errors there
virtual void import_error()
{
Cmdex& cmdex = this->get_command_executor();
cmdex.errors_lock();
Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones
hz::ErrorBase* e = 0;
// find the last relevant error.
// note: const_reverse_iterator doesn't work on gcc 3, so don't do it.
for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++iter) {
// ignore iochannel errors, they may mask the real errors
if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") {
e = (*iter)->clone();
break;
}
}
cmdex.clear_errors(false); // and clear them
cmdex.errors_unlock();
if (e) { // if error is present, alert the user
on_error_warn(e);
}
}
/// This is called when an error occurs in command executor.
/// Note: The warnings are already printed via debug_* in cmdex.
virtual void on_error_warn(hz::ErrorBase* e)
{
if (!e)
return;
// import the error only if it's relevant.
std::string error_type = e->get_type();
// ignore giochannel errors - higher level errors will be triggered, and they more user-friendly.
if (error_type == "giochannel" || error_type == "custom") {
return;
}
this->set_error_msg(e->get_message());
}
};
/// tw_cli executor without GUI support
typedef ArecaCliExecutorGeneric<CmdexSync> ArecaCliExecutor;
/// A reference-counting pointer to ArecaCliExecutor
typedef hz::intrusive_ptr<ArecaCliExecutor> ArecaCliExecutorRefPtr;
/// tw_cli executor with GUI support
typedef ArecaCliExecutorGeneric<CmdexSyncGui> ArecaCliExecutorGui;
/// A reference-counting pointer to ArecaCliExecutorGui
typedef hz::intrusive_ptr<ArecaCliExecutorGui> ArecaCliExecutorGuiRefPtr;
#endif
/// @}
+209 -96
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <sys/types.h>
#include <cerrno> // errno (not std::errno, it may be a macro)
@@ -19,7 +14,6 @@
// #include <unistd.h> // close()
#endif
#include "hz/process_signal.h" // hz::process_signal_send, win32's W*
#include "hz/tls.h"
#include "hz/tls_policy_glib.h"
#include "hz/debug.h"
@@ -42,28 +36,42 @@ extern "C" {
// callbacks
/// Child process watcher callback
// NOTE: These functions are called in a separate threads!
inline void cmdex_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
{
Cmdex::on_child_watch_handler(arg_pid, waitpid_status, data);
}
/// Child process stdout handler callback
inline gboolean cmdex_on_channel_io_stdout(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean cmdex_on_channel_io_stderr_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
/// Child process stderr handler callback
inline gboolean cmdex_on_channel_io_stderr(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
return Cmdex::on_channel_io_buffered(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean cmdex_on_channel_io_stderr_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_buffered(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
/// Child process termination timeout handler
inline void cmdex_on_buffered_source_destroy(gpointer data);
// this is executed in another thread!
inline gboolean cmdex_on_term_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
@@ -73,12 +81,12 @@ extern "C" {
}
/// Child process kill timeout handler
// this is executed in another thread!
inline gboolean cmdex_on_kill_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
self->try_stop(hz::SIGNAL_SIGKILL);
self->try_stop(hz::SIGNAL_SIGTERM);
return false; // one-time call
}
@@ -92,13 +100,13 @@ extern "C" {
bool Cmdex::execute()
{
DBG_FUNCTION_ENTER_MSG;
if (this->running_ || this->stopped_cleanup_needed()) {
ScopedLock locker(object_mutex_);
if (this->running_ || this->stopped_cleanup_needed(false))
return false;
}
cleanup_members();
clear_errors();
clear_errors(false); // don't lock
str_stdout_.clear();
str_stderr_.clear();
@@ -131,10 +139,10 @@ bool Cmdex::execute()
#endif
hz::ScopedEnv lang_env("LANG", "C", change_lang);
hz::ScopedEnv lc_numeric_env("LC_NUMERIC", "C", change_lang);
debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n");
/*
if (argvp) {
debug_out_dump("app", DBG_FUNC_MSG << "Dumping argvp:\n");
gchar** elem = argvp.get();
@@ -143,7 +151,7 @@ bool Cmdex::execute()
++elem;
}
}
*/
// Execute the command
@@ -151,7 +159,7 @@ bool Cmdex::execute()
hz::scoped_ptr<GError> spawn_error(0, g_error_free);
/*
#if defined APP_CMDEX_USE_SYNC && APP_CMDEX_USE_SYNC
#ifdef APP_CMDEX_USE_SYNC
hz::scoped_ptr<gchar*> stdout_str(0, g_free);
hz::scoped_ptr<gchar*> stderr_str(0, g_free);
@@ -186,6 +194,16 @@ bool Cmdex::execute()
// #endif
#ifdef _WIN32
// Without this timeout the program randomly says that the child's output
// is empty (happens only in windows). Sounds like a race condition, but
// doesn't seem like it's in my code (all other platforms are ok).
// Note: Testing is really hard when moving this around - the error might
// occur once in 200 runs.
// g_usleep(50*1000); // 50 msec.
#endif
#ifdef _WIN32
channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_);
channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_);
@@ -199,7 +217,7 @@ bool Cmdex::execute()
// If using locales, call g_locale_to_utf8() or g_convert() afterwards.
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
int channel_flags = ~G_IO_FLAG_NONBLOCK;
GIOFlags channel_flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
// Note about GError's here:
// What do we do? The command is already running, so let's ignore these
@@ -210,39 +228,55 @@ bool Cmdex::execute()
// a double-shutdown.
// g_io_channel_set_close_on_unref(channel_stdout_, true); // close() on fd
g_io_channel_set_encoding(channel_stdout_, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stdout_, GIOFlags(g_io_channel_get_flags(channel_stdout_) & channel_flags), 0);
g_io_channel_set_flags(channel_stdout_, channel_flags, 0);
g_io_channel_set_buffer_size(channel_stdout_, channel_stdout_buffer_size_);
}
if (channel_stderr_) {
// g_io_channel_set_close_on_unref(channel_stderr_, true); // close() on fd
g_io_channel_set_encoding(channel_stderr_, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stderr_, GIOFlags(g_io_channel_get_flags(channel_stderr_) & channel_flags), 0);
g_io_channel_set_flags(channel_stderr_, channel_flags, 0);
g_io_channel_set_buffer_size(channel_stderr_, channel_stderr_buffer_size_);
}
// Note: glib callbacks _are_ invoked in separate threads,
// so proper locking is necessary.
// This is different from gtk callbacks, which are directly invoked from main loop
// in the same thread.
GIOCondition cond = GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL);
// Channel reader callback must be called before other stuff so that the loss is minimal.
gint io_priority = G_PRIORITY_HIGH;
gint io_thread_priority = G_PRIORITY_HIGH; // channel reader callback thread must read enough so that the loss is minimal
this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_priority, cond,
&cmdex_on_channel_io_stdout, this, NULL);
// g_io_channel_unref(channel_stdout_); // g_io_add_watch_full() holds its own reference
if (this->stdout_make_str_as_available_) {
this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_thread_priority, cond,
&cmdex_on_channel_io_stdout_as_available, this, NULL);
} else {
this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_thread_priority, cond,
&cmdex_on_channel_io_stdout_buffered, this, cmdex_on_buffered_source_destroy);
}
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_priority, cond,
&cmdex_on_channel_io_stderr, this, NULL);
// g_io_channel_unref(channel_stderr_); // g_io_add_watch_full() holds its own reference
if (this->stderr_make_str_as_available_) {
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_thread_priority, cond,
&cmdex_on_channel_io_stderr_as_available, this, NULL);
} else {
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_thread_priority, cond,
&cmdex_on_channel_io_stderr_buffered, this, cmdex_on_buffered_source_destroy);
}
// If using SPAWN_DO_NOT_REAP_CHILD, this is needed to avoid zombies.
// Note: Do NOT use glibmm slot, it doesn't work here.
// The watch function is protected by mutex and won't be called before execute() exits.
// This is AFTER the channels so that it doesn't get called before the channels are attached (is this needed?).
// (the child stops being a zombie as soon as wait*() exits and this handler is called).
g_child_watch_add(this->pid_, &cmdex_child_watch_handler, this);
this->running_ = true; // the process is running now.
this->running_ = true; // process is running now.
// actually, it may be already stopped, but the watcher function is blocked
// until we exit, so the actions are serialized.
DBG_FUNCTION_EXIT_MSG;
return true;
}
@@ -252,14 +286,10 @@ bool Cmdex::execute()
// send SIGTERM(15) (terminate)
bool Cmdex::try_stop(hz::signal_t sig)
{
DBG_FUNCTION_ENTER_MSG;
#ifdef _WIN32
if (!this->running_ || this->pid_ == 0)
return false;
#else
ScopedLock locker(object_mutex_);
if (!this->running_ || this->pid_ <= 0)
return false;
#endif
// other variants: SIGHUP(1) (terminal closed), SIGINT(2) (Ctrl-C),
// SIGKILL(9) (kill).
@@ -273,45 +303,36 @@ bool Cmdex::try_stop(hz::signal_t sig)
// Possible: EPERM (no permissions), ESRCH (no such process, or zombie)
push_error(Error<int>("errno", ErrorLevel::error, errno), false);
DBG_FUNCTION_EXIT_MSG;
return false;
}
bool Cmdex::try_kill()
{
DBG_TRACE_POINT_AUTO;
return try_stop(hz::SIGNAL_SIGKILL);
}
void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec)
{
DBG_FUNCTION_ENTER_MSG;
DBG_ASSERT(term_timeout_msec == 0 || kill_timeout_msec == 0 || kill_timeout_msec > term_timeout_msec);
ScopedLock locker(object_mutex_); // member access / serialization lock
if (!this->running_) // process not running
return;
unset_stop_timeouts();
unset_stop_timeouts(false);
if (term_timeout_msec != 0)
event_source_id_term = g_timeout_add(term_timeout_msec, &cmdex_on_term_timeout, this);
if (kill_timeout_msec != 0)
event_source_id_kill = g_timeout_add(kill_timeout_msec, &cmdex_on_kill_timeout, this);
DBG_FUNCTION_EXIT_MSG;
}
void Cmdex::unset_stop_timeouts()
void Cmdex::unset_stop_timeouts(bool do_lock)
{
DBG_FUNCTION_ENTER_MSG;
ScopedLock locker(object_mutex_, do_lock); // member access / serialization lock
if (event_source_id_term) {
GSource* source_term = g_main_context_find_source_by_id(NULL, event_source_id_term);
if (source_term)
@@ -325,7 +346,6 @@ void Cmdex::unset_stop_timeouts()
g_source_destroy(source_kill);
event_source_id_kill = 0;
}
DBG_FUNCTION_EXIT_MSG;
}
@@ -333,12 +353,15 @@ void Cmdex::unset_stop_timeouts()
// executed in main thread, manually by the caller.
void Cmdex::stopped_cleanup()
{
DBG_FUNCTION_ENTER_MSG;
if (this->running_ || !this->stopped_cleanup_needed()) // huh?
// DBG_FUNCTION_ENTER_MSG;
ScopedLock locker(object_mutex_); // member access / serialization lock
if (this->running_ || !this->stopped_cleanup_needed(false)) // huh?
return;
// remove stop timeout callbacks
unset_stop_timeouts();
unset_stop_timeouts(false);
// various statuses (see waitpid (2)):
if (WIFEXITED(waitpid_status_)) { // exited normally
@@ -370,45 +393,35 @@ void Cmdex::stopped_cleanup()
cleanup_members();
this->running_ = false;
DBG_FUNCTION_EXIT_MSG;
}
// Called when child exits
// NOTE: This function is called in a separate thread!
void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
{
// DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
self->lock();
g_timer_stop(self->timer_); // stop the timer
// wait until the IO channels are closed
// while (!(self->event_source_stdout_closed && self->event_source_stderr_closed)) {
// self->unlock();
// g_usleep(75*1000); // 75ms
// self->lock();
// }
self->waitpid_status_ = waitpid_status;
self->child_watch_handler_called_ = true;
self->running_ = false; // process is not running anymore
// These are needed because Windows doesn't read the remaining data otherwise.
g_io_channel_flush(self->channel_stdout_, NULL);
on_channel_io(self->channel_stdout_, GIOCondition(0), self, channel_type_stdout);
g_io_channel_flush(self->channel_stderr_, NULL);
on_channel_io(self->channel_stderr_, GIOCondition(0), self, channel_type_stderr);
if (self->channel_stdout_) {
g_io_channel_shutdown(self->channel_stdout_, false, NULL);
g_io_channel_unref(self->channel_stdout_);
self->channel_stdout_ = 0;
}
if (self->channel_stderr_) {
g_io_channel_shutdown(self->channel_stderr_, false, NULL);
g_io_channel_unref(self->channel_stderr_);
self->channel_stderr_ = 0;
}
// Remove fd IO callbacks. They may actually be removed already (note sure about this).
// Remove fd IO callbacks. They may actually be removed already.
// This will force calling the iochannel callback (they may not be called
// otherwise at all if there was no output).
if (self->event_source_id_stdout_) {
@@ -428,9 +441,13 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
// close(self->fd_stdout_);
// close(self->fd_stderr_);
// Note: This callback is called in non-main thread!
if (self->exited_callback_)
self->exited_callback_(self->exited_callback_data_);
self->unlock();
// DBG_FUNCTION_EXIT_MSG;
}
@@ -438,11 +455,13 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
gboolean Cmdex::on_channel_io(GIOChannel* channel,
// NOTE: This function is called in a separate thread!
// This means that all data access must be guarded by mutexes, etc...
gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type)
{
// DBG_FUNCTION_ENTER_MSG;
// debug_out_dump("app", "Cmdex::on_channel_io("
// debug_out_dump("app", "Cmdex::on_channel_io_as_available("
// << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n");
bool continue_events = true;
@@ -458,14 +477,20 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel,
const gsize count = 1;
gchar buf[count] = {0};
Cmdex::ScopedLock locker(self->object_mutex_);
GIOChannel* channel = 0;
std::string* output_str = 0;
if (type == channel_type_stdout) {
channel = self->channel_stdout_;
output_str = &self->str_stdout_;
} else if (type == channel_type_stderr) {
channel = self->channel_stderr_;
output_str = &self->str_stderr_;
}
// while there's anything to read, read it
do {
hz::scoped_ptr<GError> channel_error(0, g_error_free);
@@ -494,16 +519,104 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel,
void Cmdex::cleanup_members()
namespace {
// this must be thread-local, because many instances of Cmdex may call this
// from various threads. stdout and stderr are in different threads, so they won't get mixed.
// there's one-channel-per-thread situation here, so thread-local works well.
static hz::thread_local_ptr<std::string, hz::TlsPolicyGlib> s_final_str;
}
// The problem with final_str is that it may acummulate data if this function
// is never called with (continue_events = false) condition. This may
// actually happen with terminated spawned programs.
// So, we clean final_str manually from source_destroy callback.
// This callback is executed from in the same thread as on_channel_io_buffered,
// so we get the same variable (tests show cleared s_final_str, but is this really
// so? this function is executed during on_child_watch_handler (if spawn-terminated)).
inline void cmdex_on_buffered_source_destroy(gpointer data)
{
kill_signal_sent_ = 0;
child_watch_handler_called_ = false;
pid_ = 0;
waitpid_status_ = 0;
event_source_id_stdout_ = 0;
event_source_id_stderr_ = 0;
fd_stdout_ = 0;
fd_stderr_ = 0;
// DBG_FUNCTION_ENTER_MSG;
s_final_str.reset(); // .reset(), not ->clear - we don't want memory leaks in stopped threads.
}
// NOTE: This function is called in a separate thread!
// This means that all data access must be guarded by mutexes, etc...
gboolean Cmdex::on_channel_io_buffered(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type)
{
// debug_out_dump("app", "Cmdex::on_channel_io_buffered("
// << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n");
bool continue_events = true;
if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
continue_events = false; // there'll be no more data
}
if (!s_final_str.get()) {
s_final_str.reset(new std::string);
s_final_str->reserve(g_io_channel_get_buffer_size(source));
}
// const gsize count = 4 * 1024;
// read the bytes one by one. without this, a buffered iochannel hangs while waiting for data.
// we don't use unbuffered iochannels - they may lose data on program exit.
const gsize count = 1;
static char buf[count] = {0};
// while there's anything to read, read it
do {
hz::scoped_ptr<GError> channel_error(0, g_error_free);
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(source, buf, count, &bytes_read, &channel_error.get_ref());
if (bytes_read)
s_final_str->append(buf, bytes_read);
if (channel_error) {
Cmdex::ScopedLock locker(self->object_mutex_);
self->push_error(Error<void>("giochannel", ErrorLevel::error, channel_error->message), false);
break; // stop on next invocation (is this correct?)
}
// IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable.
if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
continue_events = false;
break;
}
} while (g_io_channel_get_buffer_condition(source) & G_IO_IN);
if (!continue_events) { // need locking
Cmdex::ScopedLock locker(self->object_mutex_);
if (type == channel_type_stdout) {
// set the source to be closed (the actual closing is done when we return false).
// this is needed for the caller to wait until all the channels are stopped.
self->str_stdout_ = *s_final_str;
} else if (type == channel_type_stderr) {
self->str_stderr_ = *s_final_str;
}
// glib may re-use this thread, so do a TLS cleanup.
// also, manual cleanup is needed for policies which don't support automatic one.
// source_destroy callback already does this, but it won't hurt.
s_final_str.reset(); // this will delete the previous contents.
}
// DBG_FUNCTION_EXIT_MSG;
// false if the source should be removed, true otherwise.
return continue_events;
}
@@ -511,4 +624,4 @@ void Cmdex::cleanup_members()
/// @}
+218 -107
View File
@@ -1,57 +1,81 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef APP_CMDEX_H
#define APP_CMDEX_H
#include <glib.h>
#include <string>
#include <glib.h>
#include "hz/process_signal.h" // hz::SIGNAL_*
#include "hz/process_signal.h" // hz::SIGNAL_*, hz::process_signal_send (cpp), win32's W* (cpp)
#include "hz/error_holder.h"
#include "hz/sync.h" // hz::SyncPolicyNone
#include "hz/sync.h"
#include "hz/sync_policy_glib.h"
/// Command executor.
/// There are two ways to detect when the command exits:
/// 1. Add a callback to signal_exited.
/// 2. Manually poll stopped_cleanup_needed() (from the same thread).
/// In both cases, stopped_cleanup() must be called afterwards
/// (from the main thread).
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
/*
There are two ways to detect when the command exits:
1. Add a callback to signal_exited. The callback will be executed
in non-main thread, which means that all data access MUST be
very carefully evaluated (and locked in both threads).
2. Manually poll stopped_cleanup_needed() from the main thread.
In both cases, stopped_cleanup() must be called afterwards,
from the _main_ thread.
*/
// typedef hz::SyncPolicyGlib CmdexSyncPolicy;
typedef hz::SyncPolicyMtDefault CmdexSyncPolicy;
// inheriting from sigc::trackable automatically disconnects this class's
// slots upon destruction.
class Cmdex : public hz::ErrorHolder<CmdexSyncPolicy> {
public:
using hz::ErrorHolder<hz::SyncPolicyNone>::ptr_error_list_t; ///< Auto-deleting pointer container
using hz::ErrorHolder<hz::SyncPolicyNone>::error_list_t; ///< Error list type
using hz::ErrorHolder<CmdexSyncPolicy>::ptr_error_list_t; // auto-deleting container
using hz::ErrorHolder<CmdexSyncPolicy>::error_list_t;
typedef hz::ErrorHolder<CmdexSyncPolicy>::ErrorScopedLock ScopedLock;
typedef hz::ErrorHolder<CmdexSyncPolicy>::ErrorLockPolicy LockPolicy;
/// A function that translates the exit error code into a readable string
typedef std::string (*exit_status_translator_func_t)(int, void*);
/// A function that is called whenever a process exits.
typedef void (*exited_callback_func_t)(void*);
/// Constructor
Cmdex(exited_callback_func_t exited_cb = NULL, void* exited_cb_data = NULL)
: running_(false), kill_signal_sent_(0), child_watch_handler_called_(false),
pid_(0), waitpid_status_(0),
timer_(g_timer_new()),
event_source_id_term(0), event_source_id_kill(0),
fd_stdout_(0), fd_stderr_(0), channel_stdout_(0), channel_stderr_(0),
channel_stdout_buffer_size_(100 * 1024), channel_stderr_buffer_size_(10 * 1024), // 100K and 10K
event_source_id_stdout_(0), event_source_id_stderr_(0),
translator_func_(0), translator_func_data_(0),
exited_callback_(exited_cb), exited_callback_data_(exited_cb_data)
Cmdex(
// exit_status_translator_func_t translator_func,
exited_callback_func_t exited_cb = NULL, void* exited_cb_data = NULL
)
: object_mutex_(error_object_mutex_),
running_(false), kill_signal_sent_(0), child_watch_handler_called_(false),
pid_(0), waitpid_status_(0),
timer_(g_timer_new()),
event_source_id_term(0), event_source_id_kill(0),
fd_stdout_(0), fd_stderr_(0), channel_stdout_(0), channel_stderr_(0),
channel_stdout_buffer_size_(100 * 1024), channel_stderr_buffer_size_(10 * 1024), // 100K and 10K
event_source_id_stdout_(0), event_source_id_stderr_(0),
// Don't set false here (in any of these) - there's a bug in windows which causes
// output to be completely swallowed at random times.
stdout_make_str_as_available_(true), stderr_make_str_as_available_(true),
translator_func_(0), translator_func_data_(0),
exited_callback_(exited_cb), exited_callback_data_(exited_cb_data)
{ }
/// Destructor. Don't destroy this object unless the child has exited. It will leak stuff
/// and possibly crash, etc... .
// Please don't destroy this object unless the child has exited. It will leak stuff
// and possibly crash, etc... .
~Cmdex()
{
// This will help if object is destroyed after the command has exited, but before
@@ -65,10 +89,10 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
}
/// Set the command to execute. Call before execute().
/// Note: The command and the arguments _must_ be shell-escaped.
/// Use g_shell_quote() or Glib::shell_quote(). Note that each argument
/// must be escaped separately.
// Call before execute.
// Note: The command and the arguments _must_ be shell-escaped.
// Use g_shell_quote() or Glib::shell_quote(). Note that each argument
// must be escaped separately.
void set_command(const std::string& command_exec, const std::string& command_args)
{
command_exec_ = command_exec;
@@ -76,70 +100,77 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
}
/// Launch the command.
// launch the command.
bool execute();
/// Send SIGTERM(15) (terminate) to the child process.
/// Use only after execute(). Using it after the command has exited has no effect.
// send SIGTERM(15) (terminate).
// use after execute(). using it after the command has exited has no effect.
bool try_stop(hz::signal_t sig = hz::SIGNAL_SIGTERM);
/// Send SIGKILL(9) (kill) to the child process. Same as
/// try_stop(hz::SIGNAL_SIGKILL).
/// Note that SIGKILL cannot be overridden in child process.
bool try_kill();
// Send SIGKILL(9) (kill). This signal cannot be overridden in child process.
// use after execute(). using it after the command has exited has no effect.
bool try_kill()
{
return try_stop(hz::SIGNAL_SIGKILL);
}
/// Set a timeout (since call to this function) to terminate the child process,
/// kill it or both (use 0 to ignore the parameter).
/// The timeouts will be unset automatically when the command exits.
/// This has an effect only if the command is running (after execute()).
// set a timeout (since call to this function) to terminate, kill or both (use 0 to ignore the parameter).
// the timeouts will be unset automatically when the command exits.
// This has an effect only if the command is running (after execute()).
void set_stop_timeouts(int term_timeout_msec = 0, int kill_timeout_msec = 0);
/// Unset the terminate / kill timeouts. This will stop the timeout counters.
/// This has an effect only if the command is running (after execute()).
void unset_stop_timeouts();
// Unset timeouts. This will stop the timeout counters.
// This has an effect only if the command is running (after execute()).
// do_lock is kind of private and indicates that mutex should be locked prior to
// accessing the object members.
void unset_stop_timeouts(bool do_lock = true);
/// If stopped_cleanup_needed() returned true, call this. The command
/// should be exited by this time. Must be called before the next execute().
// If stopped_cleanup_needed() returned true, call this. The command
// should be exited by this time. Must be called before the next execute().
void stopped_cleanup();
/// Returns true if command has stopped.
/// Call repeatedly in a waiting function, after execute().
/// When it returns true, call stopped_cleanup().
bool stopped_cleanup_needed()
// Returns true if command has stopped.
// Call repeatedly in a waiting function, after execute(). When returns true, call stopped_cleanup().
// do_lock is private, set to false only if you already hold the object lock.
bool stopped_cleanup_needed(bool do_lock = true)
{
ScopedLock locker(object_mutex_, do_lock);
return (child_watch_handler_called_);
}
/// Check if the process is running. Note that if this returns false, it doesn't mean that
/// the io channels have been closed or that the data may be read safely. Poll
/// stopped_cleanup_needed() instead.
// The process is running. Note that if this returns false, it doesn't mean that
// the io channels have been closed or that the data may be read safely. Poll
// stopped_cleanup_needed() instead.
bool is_running() const
{
ScopedLock locker(object_mutex_);
return running_;
}
/// Call this before execution. There is a race-like condition - when the command
/// outputs something, the io channel reads it from fd to its buffer and the event
/// source callback is called. If the command dies, the io channel callback reads
/// the remaining data to the channel buffer.
/// Since the event source callbacks (which read from the buffer and empty it)
/// happen rather sporadically (from the glib loop), the buffer may not get read and
/// emptied at all (before the command exits). This is why it's necessary to have
/// a buffer size which potentially can hold _all_ the command output.
/// A way to fight this is to increase event source priority (which may not help).
/// Another way is to delay the command exit so that the event source callback
/// catches on and reads the buffer.
// Use 0 to ignore the parameter. Call this before execute().
// Call this before execution. There is a race-like condition - when the command
// outputs something, the io channel reads it from fd to its buffer and the event
// source callback is called. If the command dies, the io channel thread reads
// the remaining data to the channel buffer.
// Since the event source callbacks (which read from the buffer and empty it)
// happen rather sporadically in another thread, the buffer may not get read and
// emptied at all (before the command exits). This is why it's necessary to have
// a buffer size which potentially can hold _all_ the command output.
// A way to fight this is to increase event source thread priority (which may not help).
// Another way is to delay the command exit so that the event source thread
// catches on and reads the buffer.
// Use 0 to ignore the parameter. Call before execute().
void set_buffer_sizes(int stdout_buffer_size = 0, int stderr_buffer_size = 0)
{
ScopedLock locker(object_mutex_);
if (stdout_buffer_size)
channel_stdout_buffer_size_ = stdout_buffer_size; // 100K by default
if (stderr_buffer_size)
@@ -147,12 +178,29 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
}
// Set the flags for child stdout and stderr. If stdout_str_as_available is true,
// then get_stdout_str() will return the stdout string as it becomes available,
// while false will make it available only after the command exits. Note that
// if the command outputs a lot of data in a very short period of time and
// then exits, false is much better choice because it's much faster.
// If you want to monitor the data as it becomes available and clean it to
// save some memory, use true.
// Call before execute().
void set_str_available(bool stdout_str_as_available = false, bool stderr_str_as_available = true)
{
ScopedLock locker(object_mutex_);
stdout_make_str_as_available_ = stdout_str_as_available;
stderr_make_str_as_available_ = stderr_str_as_available;
}
/// If stdout_make_str_as_available_ is false, call this after stopped_cleanup(),
/// before next execute(). If it's true, you may call this before the command has
/// stopped, but it will decrease performance significantly.
// If stdout_make_str_as_available_ is false, call this after stopped_cleanup(),
// before next execute(). If it's true, you may call this before the command has
// stopped, but it will decrease performance significantly.
std::string get_stdout_str(bool clear_existing = false)
{
ScopedLock locker(object_mutex_);
// debug_out_dump("app", str_stdout_);
if (clear_existing) {
std::string ret = str_stdout_;
@@ -163,9 +211,10 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
}
/// See notes on get_stdout_str().
// See notes on get_stdout_str().
std::string get_stderr_str(bool clear_existing = false)
{
ScopedLock locker(object_mutex_);
if (clear_existing) {
std::string ret = str_stderr_;
str_stderr_.clear();
@@ -175,36 +224,68 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
}
/// Return execution time, in seconds. Call this after execute().
// Return execution time, in seconds. Call after execute().
double get_execution_time()
{
ScopedLock locker(object_mutex_);
gulong microsec = 0;
return g_timer_elapsed(timer_, &microsec);
}
/// Set exit status translator callback, disconnecting the old one.
/// Call only before execute().
// don't return sigc::connection - it's better for thread-safety this way.
// this will disconnect earlier slots. Call before execute().
void set_exit_status_translator(exit_status_translator_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
translator_func_ = func;
translator_func_data_ = user_data;
}
/// Set exit notifier callback, disconnecting the old one.
/// You can poll stopped_cleanup_needed() instead of using this function.
// NOTE: The callback will be called in non-main thread! (the call is enclosed
// in lock/unlock).
// Don't use this unless absolutely necessary, use stopped_cleanup_needed()
// polling instead.
void set_exited_callback(exited_callback_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
exited_callback_ = func;
exited_callback_data_ = user_data;
}
// use lock() / unlock() if using members of this class outside of it.
void lock()
{
errors_lock(); // defer to parent's mutex - we use it for the whole object.
}
void unlock()
{
errors_unlock();
}
// protect all member variable access by this mutex, as well as
// serialize event callback execution (in respect of main thread too).
// it's possible to implement mutexes for each member individually, but
// it's impractical.
// mutable allows the definition of const member functions - they only
// read the data, but do it in a lock-protected manner.
// mutable Glib::Mutex object_mutex; // aka access serializer mutex
// use ErrorHolder's mutex
// these are sorta-private
/// Channel type, for passing to callbacks
// for passing to callback
enum channel_t {
channel_type_stdout,
channel_type_stderr
@@ -213,65 +294,95 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
// Callbacks (Note: These are called by the real callbacks)
/// Child watch handler
static void on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data);
/// Channel I/O handler
static gboolean on_channel_io(GIOChannel* channel, GIOCondition cond, Cmdex* self, Cmdex::channel_t type);
static gboolean on_channel_io_buffered(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type);
static gboolean on_channel_io_as_available(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type);
private:
/// Clean up the member variables and shut down the channels if needed.
void cleanup_members();
// The object MUST be locked by the caller.
void cleanup_members()
{
// ScopedLock locker(object_mutex_);
kill_signal_sent_ = 0;
child_watch_handler_called_ = false;
pid_ = 0;
waitpid_status_ = 0;
event_source_id_stdout_ = 0;
event_source_id_stderr_ = 0;
fd_stdout_ = 0;
fd_stderr_ = 0;
if (channel_stdout_) {
g_io_channel_shutdown(channel_stdout_, false, NULL);
g_io_channel_unref(channel_stdout_);
channel_stdout_ = 0;
}
if (channel_stderr_) {
g_io_channel_shutdown(channel_stderr_, false, NULL);
g_io_channel_unref(channel_stderr_);
channel_stderr_ = 0;
}
}
mutable ErrorLockPolicy::Mutex& object_mutex_; // reference to parent's member
// default command and its args. std::strings, not ustrings.
std::string command_exec_; /// Binary name to execute. NOT affected by cleanup_members().
std::string command_args_; /// Arguments that always go with the binary. NOT affected by cleanup_members().
std::string command_exec_; // binary name. NOT affected by cleanup().
std::string command_args_; // args that always go with binary. NOT affected by cleanup().
bool running_; ///< If true, the child process is running now. NOT affected by cleanup_members().
int kill_signal_sent_; ///< If non-zero, the process has been sent this signal to terminate
bool child_watch_handler_called_; ///< true after child_watch_handler callback, before stopped_cleanup().
bool running_; // child process is running now. NOT affected by cleanup().
int kill_signal_sent_; // command has been sent this signal to terminate
bool child_watch_handler_called_; // true after child_watch_handler callback, before stopped_cleanup().
GPid pid_; ///< Process ID. int in Unix, pointer in win32
int waitpid_status_; ///< After the command is stopped, before cleanup, this will be available (waitpid() status).
GPid pid_; // int in Unix, pointer in win32
int waitpid_status_; // after the command is stopped, before cleanup, this will be available.
GTimer* timer_; ///< Keeps track of elapsed time since command execution. Value is not used by this class, but may be handy.
GTimer* timer_; // keep track of elapsed time since command execution. not used by this class, but may be handy.
int event_source_id_term; ///< Timeout event source ID for SIGTERM.
int event_source_id_kill; ///< Timeout event source ID for SIGKILL.
int event_source_id_term;
int event_source_id_kill;
int fd_stdout_; ///< stdout file descriptor
int fd_stderr_; ///< stderr file descriptor
int fd_stdout_;
int fd_stderr_;
GIOChannel* channel_stdout_; ///< stdout channel
GIOChannel* channel_stderr_; ///< stderr channel
GIOChannel* channel_stdout_;
GIOChannel* channel_stderr_;
int channel_stdout_buffer_size_; ///< stdout channel buffer size. NOT affected by cleanup_members().
int channel_stderr_buffer_size_; ///< stderr channel buffer size. NOT affected by cleanup_members().
int channel_stdout_buffer_size_; // NOT affected by cleanup().
int channel_stderr_buffer_size_; // NOT affected by cleanup().
int event_source_id_stdout_; ///< IO watcher event source ID for stdout
int event_source_id_stderr_; ///< IO watcher event source ID for stderr
int event_source_id_stdout_;
int event_source_id_stderr_;
std::string str_stdout_; ///< stdout data read during execution. NOT affected by cleanup_members().
std::string str_stderr_; ///< stderr data read during execution. NOT affected by cleanup_members().
int stdout_make_str_as_available_; // NOT affected by cleanup().
int stderr_make_str_as_available_; // NOT affected by cleanup().
std::string str_stdout_; // NOT affected by cleanup().
std::string str_stderr_; // NOT affected by cleanup().
// signals
// convert command exit status to message string
exit_status_translator_func_t translator_func_; ///< Exit status translator function. NOT affected by cleanup_members().
void* translator_func_data_; ///< Data to supply to the exit status translator function.
exit_status_translator_func_t translator_func_; // NOT affected by cleanup().
void* translator_func_data_;
// "command exited" signal callback.
exited_callback_func_t exited_callback_; ///< Exit notifier function. NOT affected by cleanup_members().
void* exited_callback_data_; ///< Data to supply to the exit notifier function.
// NOTE: This will be called in separate thread!
exited_callback_func_t exited_callback_; // NOT affected by cleanup().
void* exited_callback_data_;
};
+2 -14
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <glib.h> // g_usleep()
@@ -17,16 +12,13 @@
namespace {
/// "Execution finished" signal
static sigc::signal<void, const CmdexSyncCommandInfo&> s_cmdex_sync_signal_execute_finish;
/// Mutex for "Execution finished" signal
static hz::SyncPolicyMtDefault::Mutex s_cmdex_sync_signal_execute_finish_mutex;
}
cmdex_signal_execute_finish_type cmdex_sync_signal_execute_finish()
{
return cmdex_signal_execute_finish_type(s_cmdex_sync_signal_execute_finish,
@@ -95,10 +87,8 @@ bool CmdexSync::execute()
}
// Without this, no event sources will be processed and the program will
// without this, no event sources will be processed and the program will
// hang waiting for the child to exit (the watch handler won't be called).
// Note: If you have an idle callback, g_main_context_pending() will
// always return true (until the idle callback returns false and unregisters itself).
while(g_main_context_pending(NULL)) {
g_main_context_iteration(NULL, false);
}
@@ -145,5 +135,3 @@ void CmdexSync::import_error()
/// @}
+69 -99
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_CMDEX_SYNC_H
#define APP_CMDEX_SYNC_H
@@ -25,65 +20,64 @@
/// Information about finished command (structure for storing it).
/// Supports intrusive_ptr for memory-efficient storage.
// Information about finished command (structure for storing).
// Supports intrusive_ptr for memory-efficient storage.
struct CmdexSyncCommandInfoCopy : public hz::intrusive_ptr_referenced {
CmdexSyncCommandInfoCopy(const std::string& c, const std::string& p,
const std::string& so, const std::string& se, const std::string& em)
: command(c), parameters(p), std_output(so), std_error(se), error_msg(em)
{ }
std::string command; ///< Executed command
std::string parameters; ///< Command parameters
std::string std_output; ///< Stdout data
std::string std_error; ///< Stderr data
std::string error_msg; ///< Execution error message
std::string command;
std::string parameters;
std::string std_output;
std::string std_error;
std::string error_msg;
};
/// A reference-counting pointer to CmdexSyncCommandInfoCopy
typedef hz::intrusive_ptr<CmdexSyncCommandInfoCopy> CmdexSyncCommandInfoRefPtr;
/// Information about a finished command.
// Information about finished command.
struct CmdexSyncCommandInfo {
CmdexSyncCommandInfo(const std::string& c, const std::string& p,
const std::string& so, const std::string& se, const std::string& em)
: command(c), parameters(p), std_output(so), std_error(se), error_msg(em)
{ }
/// Make a copy of this structure for storage.
// make a copy for storage.
CmdexSyncCommandInfoRefPtr copy() const
{
return new CmdexSyncCommandInfoCopy(command, parameters, std_output, std_error, error_msg);
}
const std::string& command; ///< Executed command
const std::string& parameters; ///< Command parameters
const std::string& std_output; ///< Stdout data
const std::string& std_error; ///< Stderr data
const std::string& error_msg; ///< Execution error message
const std::string& command;
const std::string& parameters;
const std::string& std_output;
const std::string& std_error;
const std::string& error_msg;
};
/// cmdex_sync_signal_execute_finish() return type, mt-synchronized signal.
typedef hz::sync_lock_ptr< sigc::signal<void, const CmdexSyncCommandInfo&>&,
hz::SyncPolicyMtDefault::ScopedLock > cmdex_signal_execute_finish_type;
/// This signal is emitted every time execute() finishes.
// Emitted every time execute() finishes.
cmdex_signal_execute_finish_type cmdex_sync_signal_execute_finish();
/// Synchronous Cmdex (command executor) with ticking support.
// Synchronious Cmdex (command executor) with ticking
class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
public:
/// Constructor
CmdexSync(const std::string& command_exec, const std::string& command_args)
{
construct();
@@ -91,19 +85,24 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
}
/// Constructor
CmdexSync()
{
construct();
}
/// Virtual destructor
void construct()
{
forced_kill_timeout_msec = 3 * 1000; // 3 sec default
running_msg_ = "Running %s..."; // %s will be replaced by command basename
set_error_header("An error occurred while executing the command:\n\n");
}
virtual ~CmdexSync()
{ }
/// Set command to execute and its parameters
void set_command(const std::string& command_name, const std::string& command_args)
{
cmdex_.set_command(command_name, command_args);
@@ -113,102 +112,96 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
}
/// Get command to execute
std::string get_command_name() const
{
return command_name_;
}
/// Get command arguments
std::string get_command_args() const
{
return command_args_;
}
/// Execute the command. The function will return only after the command exits.
/// Calls signal_execute_tick signal repeatedly while doing stuff.
/// Note: If the command _was_ executed, but there was an error,
/// this will return true. Check get_error_msg() for emptiness.
/// \c return false if failed to execute, true otherwise.
// Returns false if failed to execute, true otherwise.
// The function will return only after the command exits.
// Calls signal_execute_tick signal repeatedly while doing stuff.
// Note: If the command _was_ executed, but there was an error,
// this will return true. Check get_error_msg() emptiness.
virtual bool execute();
/// Set timeout (in ms) to send SIGKILL after sending SIGTERM.
/// Used if manual stop was requested through ticker.
// Timeout to send SIGKILL after sending SIGTERM.
// Used if manual stop was requested through ticker.
void set_forced_kill_timeout(int timeout_msec)
{
forced_kill_timeout_msec = timeout_msec;
}
/// Try to stop the process. Call this from ticker slot while executing.
// Call from slot while executing.
bool try_stop(hz::signal_t sig = hz::SIGNAL_SIGTERM)
{
return cmdex_.try_stop(sig);
}
/// Same as try_stop(hz::SIGNAL_SIGKILL).
// Call from slot while executing.
bool try_kill()
{
return cmdex_.try_kill();
}
/// Set a timeout (since call to this function) to terminate, kill or both (use 0 to ignore the parameter).
/// the timeouts will be unset automatically when the command exits.
/// Call from ticker slot while executing.
// Call from slot while executing.
// set a timeout (since call to this function) to terminate, kill or both (use 0 to ignore the parameter).
// the timeouts will be unset automatically when the command exits.
void set_stop_timeouts(int term_timeout_msec = 0, int kill_timeout_msec = 0)
{
cmdex_.set_stop_timeouts(term_timeout_msec, kill_timeout_msec);
}
/// Unset terminate / kill timeouts. This will stop the timeout counters.
/// Call from ticker slot while executing.
// Call from slot while executing.
// Unset timeouts. This will stop the timeout counters.
void unset_stop_timeouts()
{
cmdex_.unset_stop_timeouts();
}
/// Check if the child process is running. See Cmdex::is_running().
/// Call from ticker slot while executing.
// Call from slot while executing.
bool is_running() const
{
return cmdex_.is_running();
}
/// See Cmdex::set_buffer_sizes() for details. Call this before execute().
// Use 0 to ignore the parameter. Call before execute().
// See Cmdex for details.
void set_buffer_sizes(int stdout_buffer_size = 0, int stderr_buffer_size = 0)
{
cmdex_.set_buffer_sizes(stdout_buffer_size, stderr_buffer_size);
}
/// See Cmdex::get_stdout_str() for details.
// Call before execute().
// See Cmdex for details.
void set_str_available(bool stdout_str_as_available = false, bool stderr_str_as_available = true)
{
cmdex_.set_str_available(stdout_str_as_available, stderr_str_as_available);
}
std::string get_stdout_str(bool clear_existing = false)
{
return cmdex_.get_stdout_str(clear_existing);
}
/// See Cmdex::get_stderr_str() for details.
std::string get_stderr_str(bool clear_existing = false)
{
return cmdex_.get_stderr_str(clear_existing);
}
/// See Cmdex::set_exit_status_translator() for details.
void set_exit_status_translator(Cmdex::exit_status_translator_func_t func, void* user_data)
{
cmdex_.set_exit_status_translator(func, user_data);
}
/// Get command execution error message. If \c with_header
/// is true, a header set using set_error_header() will be displayed first.
std::string get_error_msg(bool with_header = false) const
{
if (with_header)
@@ -216,22 +209,16 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
return error_msg_;
}
/// Set a message to display when running. %s in \c msg will be replaced by the command.
void set_running_msg(const std::string& msg)
{
running_msg_ = msg;
}
/// Set error header string. See get_error_msg()
void set_error_header(const std::string& msg)
{
error_header_ = msg;
}
/// Get error header string. See get_error_msg()
std::string get_error_header()
{
return error_header_;
@@ -241,39 +228,28 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
// ----------------- Signals
/// Status flags for signal_execute_tick slots, along with possible return values.
// status flags for signal_execute_tick slots, along with possible return values.
enum tick_status_t {
status_starting, ///< Return status will indicate whether to proceed with the execution
status_failed, ///< The execution failed
status_running, ///< Return status will indicate whether to abort the execution
status_stopping, ///< The child has been sent a signal
status_stopped ///< The child exited
status_starting, // return status will indicate whether to proceed with the execution
status_failed, // the execution failed
status_running, // return status will indicate whether to abort the execution
status_stopping, // the child has been sent a signal
status_stopped // the child exited
};
/// This signal is emitted whenever something happens with the execution
/// (the status is changed), and periodically while the process is running.
// this signal is emitted whenever something happens with the execution.
sigc::signal<bool, tick_status_t> signal_execute_tick;
protected:
/// Initialize the members (constructor calls this)
void construct()
{
forced_kill_timeout_msec = 3 * 1000; // 3 sec default
running_msg_ = "Running %s..."; // %s will be replaced by command basename
set_error_header("An error occurred while executing the command:\n\n");
}
/// Import the last error from cmdex_ and clear all errors there.
// import the last error from cmdex_ and clear all errors there
virtual void import_error();
/// The warnings are already printed via debug_* in cmdex.
/// Override if needed.
// The warnings are already printed via debug_* in cmdex.
// Override if needed.
virtual void on_error_warn(hz::ErrorBase* e)
{
if (e) {
@@ -282,41 +258,37 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
}
/// Set error message
void set_error_msg(const std::string& error_msg)
{
error_msg_ = error_msg;
}
/// Get "running" message
std::string get_running_msg() const
{
return running_msg_;
}
/// Get command executor object
Cmdex& get_command_executor()
{
return cmdex_;
}
private:
Cmdex cmdex_; ///< Command executor
Cmdex cmdex_;
std::string command_name_; ///< Command name
std::string command_args_; ///< Command arguments
std::string command_name_;
std::string command_args_;
std::string running_msg_; ///< "Running" message (to show in the dialogs, etc...)
std::string running_msg_; // message to show in the dialogs, etc...
int forced_kill_timeout_msec; ///< Kill timeout in ms.
int forced_kill_timeout_msec;
std::string error_msg_; ///< Execution error message
std::string error_header_; ///< The error message may have this prepended to it.
hz::ErrorBase* error_; ///< Error holder.
std::string error_msg_;
std::string error_header_;
hz::ErrorBase* error_;
};
@@ -326,5 +298,3 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
#endif
/// @}
+22 -15
View File
@@ -1,20 +1,15 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <gtkmm.h> // Gtk::Main
#include <gdkmm.h>
#include <gtkmm/main.h> // Gtk::Main
#include "hz/fs_path.h"
#include "hz/string_sprintf.h"
#include "app_gtkmm_features.h" // APP_GTKMM_CHECK_VERSION
#include "cmdex_sync_gui.h"
@@ -28,8 +23,15 @@ bool CmdexSyncGui::execute()
}
#define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_OTHER
#define CMDEX_DIALOG_HINT_TYPE Gdk::WINDOW_TYPE_HINT_DIALOG
#if APP_GTKMM_CHECK_VERSION(2, 10, 0)
// these are available since gtkmm 2.10
#define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_OTHER
// not present in Gdk:: in 2.10 (it's in 2.12), use we have to use GDK_
#define CMDEX_DIALOG_HINT_TYPE GDK_WINDOW_TYPE_HINT_NOTIFICATION
#else
#define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_INFO
#define CMDEX_DIALOG_HINT_TYPE GDK_WINDOW_TYPE_HINT_UTILITY
#endif
@@ -54,7 +56,9 @@ Gtk::MessageDialog* CmdexSyncGui::create_running_dialog(Gtk::Window* parent, con
&CmdexSyncGui::on_running_dialog_response));
running_dialog_->set_decorated(false);
running_dialog_->set_deletable(false);
#if APP_GTKMM_CHECK_VERSION(2, 10, 0)
running_dialog_->set_deletable(false); // since 2.10
#endif
running_dialog_->set_skip_pager_hint(true);
running_dialog_->set_skip_taskbar_hint(true);
running_dialog_->set_type_hint(Gdk::WindowTypeHint(CMDEX_DIALOG_HINT_TYPE));
@@ -88,11 +92,11 @@ void CmdexSyncGui::show_hide_dialog(bool show)
void CmdexSyncGui::update_dialog_show_timer()
{
double timeout = 2.; // 2 sec for normal dialogs
double timeout = 0.600; // 0.6 sec for normal dialogs
if (running_dialog_abort_mode_)
timeout = 0.4; // 0.4 sec for aborting... dialogs
timeout = 0.150; // 0.15 sec for aborting... dialogs
if (!running_dialog_shown_ && running_dialog_timer_.elapsed() > timeout) {
if (!running_dialog_shown_ && running_dialog_timer_.elapsed() > 1.000) { // 1 sec
// without first making it sensitive, the "whole label selected" problem may occur.
running_dialog_->set_response_sensitive(Gtk::RESPONSE_CANCEL, true);
@@ -142,6 +146,9 @@ void CmdexSyncGui::set_running_dialog_abort_mode(bool aborting)
// this function is called every time something happens
// with the executor.
bool CmdexSyncGui::execute_tick_func(tick_status_t status)
{
if (status == status_starting) {
@@ -233,4 +240,4 @@ bool CmdexSyncGui::execute_tick_func(tick_status_t status)
/// @}
+29 -35
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef APP_CMDEX_SYNC_GUI_H
#define APP_CMDEX_SYNC_GUI_H
@@ -21,78 +16,78 @@
/// Same as CmdexSync, but with GTK UI support.
/// This one is noncopyable, because we can't copy the dialogs, etc...
// CmdexSync with GUI support
// same as CmdexSync, but with GTK UI stuff.
// this one is noncopyable, because we don't copy the dialogs, etc...
class CmdexSyncGui : public CmdexSync, public hz::noncopyable {
public:
/// Constructor
CmdexSyncGui(const std::string& cmd, const std::string& cmdargs)
: CmdexSync(cmd, cmdargs), execution_running_(false), should_abort_(false),
: CmdexSync(cmd, cmdargs), execution_running_(false), should_abort_(false),
running_dialog_(0), running_dialog_shown_(false), running_dialog_abort_mode_(false)
{
signal_execute_tick.connect(sigc::mem_fun(*this, &CmdexSyncGui::execute_tick_func));
}
/// Constructor
CmdexSyncGui()
: execution_running_(false), should_abort_(false),
: execution_running_(false), should_abort_(false),
running_dialog_(0), running_dialog_shown_(false), running_dialog_abort_mode_(false)
{
signal_execute_tick.connect(sigc::mem_fun(*this, &CmdexSyncGui::execute_tick_func));
}
/// Destructor
~CmdexSyncGui()
{
delete running_dialog_;
}
// Reimplemented from CmdexSync
virtual bool execute();
/// UI callbacks may use this to abort execution.
// UI callbacks may use this to abort execution.
void set_should_abort()
{
should_abort_ = true;
}
/// Create a "running" dialog or return already existing one.
/// The dialog will be auto-created and displayed on execute().
/// You need the function only if you intend to modify it before execute().
// create a dialog or return already existing one.
// the dialog will be auto-created and displayed on execute().
// you need the function only if you intend to modify it before execute().
Gtk::MessageDialog* create_running_dialog(Gtk::Window* parent = 0, const Glib::ustring& msg = "");
/// Return the "running" dialog
Gtk::MessageDialog* get_running_dialog()
{
return running_dialog_;
}
/// Show or hide the "running" dialog.
/// This actually shows the dialog only after some time has passed,
/// to avoid very quick show/hide in case the command exits very quickly.
// this actually shows the dialog only after some time has passed,
// to avoid very quick show/hide.
void show_hide_dialog(bool show);
/// This function is called from ticker function in running mode
/// to show the dialog when requested time elapses.
// called from ticker function in running mode, to show the dialog when requested time elapses.
void update_dialog_show_timer();
/// Switch the dialog to "aborting..." mode
// switch the dialog to "Aborting..." mode
void set_running_dialog_abort_mode(bool aborting);
private:
/// Dialog response callback.
// dialog callback.
void on_running_dialog_response(int response_id)
{
// Try to abort if Cancel was clicked
@@ -101,17 +96,18 @@ class CmdexSyncGui : public CmdexSync, public hz::noncopyable {
}
/// This function is attached to CmdexSync::signal_execute_tick.
// this function is called every time something happens
// with the executor.
bool execute_tick_func(tick_status_t status);
bool execution_running_; ///< If true, the execution is still in progress
bool should_abort_; ///< GUI callbacks may set this to abort the execution
bool execution_running_;
bool should_abort_; // GUI callbacks may set this to abort the execution
Gtk::MessageDialog* running_dialog_; ///< "Running" dialog
bool running_dialog_shown_; ///< If true, the "running" dialog is visible
bool running_dialog_abort_mode_; ///< If true, the "running" dialog is in "aborting..." mode.
Glib::Timer running_dialog_timer_; ///< "Running" dialog show timer.
Gtk::MessageDialog* running_dialog_;
bool running_dialog_shown_;
bool running_dialog_abort_mode_;
Glib::Timer running_dialog_timer_;
};
@@ -122,5 +118,3 @@ class CmdexSyncGui : public CmdexSync, public hz::noncopyable {
#endif
/// @}
-67
View File
@@ -1,67 +0,0 @@
/**************************************************************************
Copyright:
(C) 2011 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include "hz/debug.h"
#include "executor_factory.h"
#include "smartctl_executor_gui.h"
#include "cli_executors.h"
ExecutorFactory::ExecutorFactory(bool use_gui, Gtk::Window* parent)
: use_gui_(use_gui), parent_(parent)
{ }
hz::intrusive_ptr<CmdexSync> ExecutorFactory::create_executor(ExecutorFactory::Type type)
{
switch (type) {
case ExecutorSmartctl:
{
if (use_gui_) {
SmartctlExecutorGuiRefPtr ex = SmartctlExecutorGuiRefPtr(new SmartctlExecutorGui());
ex->create_running_dialog(parent_); // dialog parent
return ex;
}
return SmartctlExecutorRefPtr(new SmartctlExecutor());
}
case ExecutorTwCli:
{
if (use_gui_) {
TwCliExecutorGuiRefPtr ex = TwCliExecutorGuiRefPtr(new TwCliExecutorGui());
ex->create_running_dialog(parent_); // dialog parent
return ex;
}
return TwCliExecutorRefPtr(new TwCliExecutor());
}
case ExecutorArecaCli:
{
if (use_gui_) {
ArecaCliExecutorGuiRefPtr ex = ArecaCliExecutorGuiRefPtr(new ArecaCliExecutorGui());
ex->create_running_dialog(parent_); // dialog parent
return ex;
}
return ArecaCliExecutorRefPtr(new ArecaCliExecutor());
}
}
DBG_ASSERT(0);
return hz::intrusive_ptr<CmdexSync>();
}
/// @}
-64
View File
@@ -1,64 +0,0 @@
/**************************************************************************
Copyright:
(C) 2011 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef EXECUTOR_FACTORY_H
#define EXECUTOR_FACTORY_H
#include "cmdex_sync.h"
#include "hz/intrusive_ptr.h"
// Forward declaration
namespace Gtk {
class Window;
}
/// This class allows you to create new executors for different commands,
/// without carrying the GUI/CLI stuff manually.
class ExecutorFactory : public hz::intrusive_ptr_referenced {
public:
/// Executor type for create_executor()
enum Type {
ExecutorSmartctl,
ExecutorTwCli,
ExecutorArecaCli
};
/// Constructor. If \c use_gui is true, specify \c parent for the GUI dialogs.
ExecutorFactory(bool use_gui, Gtk::Window* parent = 0);
/// Create a new executor instance according to \c type and the constructor parameters.
hz::intrusive_ptr<CmdexSync> create_executor(Type type);
private:
bool use_gui_; ///< Whether to construct GUI executors or not.
Gtk::Window* parent_; ///< Parent window for dialogs
};
/// A reference-counting pointer to ExecutorFactory
typedef hz::intrusive_ptr<ExecutorFactory> ExecutorFactoryRefPtr;
#endif
/// @}
+14 -14
View File
@@ -1,21 +1,23 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <gtkmm/messagedialog.h>
#include <gtkmm/dialog.h>
#include <gtkmm/label.h>
#include <gtkmm/entry.h>
#include <gtkmm/stock.h>
#include <gtkmm/box.h>
#include "app_pango_utils.h" // app_pango_strip_markup()
#include "gui_utils.h"
namespace {
namespace
{
/// Show a message dialog
inline void show_dialog(const std::string& message, const std::string& sec_message,
Gtk::Window* parent, Gtk::MessageType type, bool sec_msg_markup)
{
@@ -113,7 +115,7 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
+ (sec_message.empty() ? "\n" : "") + "</b></big>");
main_label.set_line_wrap(true);
main_label.set_selectable(true);
main_label.set_alignment(Gtk::ALIGN_START);
main_label.set_alignment(0.0, 0.0);
Gtk::Label sec_label;
if (sec_msg_markup) {
@@ -123,13 +125,13 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
}
sec_label.set_line_wrap(true);
sec_label.set_selectable(true);
sec_label.set_alignment(Gtk::ALIGN_START);
sec_label.set_alignment(0.0, 0.0);
Gtk::Entry input_entry;
input_entry.set_activates_default(true);
Gtk::Box vbox;
Gtk::VBox vbox;
vbox.set_spacing(12);
vbox.pack_start(main_label, false, false, 0);
vbox.pack_start(sec_label, true, true, 0);
@@ -146,7 +148,7 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
Gtk::Button ok_button(Gtk::Stock::OK);
ok_button.set_can_default(true);
ok_button.set_flags(ok_button.get_flags() | Gtk::CAN_DEFAULT);
ok_button.show_all();
dialog.add_action_widget(ok_button, Gtk::RESPONSE_OK);
ok_button.grab_default(); // make it the default widget
@@ -168,5 +170,3 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
/// @}
+4 -21
View File
@@ -1,58 +1,43 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef GUI_UTILS_H
#define GUI_UTILS_H
#include <string>
#include <gtkmm.h>
#include <gtkmm/window.h>
// These functions won't return until the dialogs are closed.
// Messages must not contain any markup.
// Message must not contain any markup.
/// Show an error dialog
void gui_show_error_dialog(const std::string& message, Gtk::Window* parent = 0);
/// Show an error dialog with a (possibly markupped) secondary message
void gui_show_error_dialog(const std::string& message, const std::string& sec_message,
Gtk::Window* parent = 0, bool sec_msg_markup = false);
/// Show a warning dialog
void gui_show_warn_dialog(const std::string& message, Gtk::Window* parent = 0);
/// Show a warning dialog with a (possibly markupped) secondary message
void gui_show_warn_dialog(const std::string& message, const std::string& sec_message,
Gtk::Window* parent = 0, bool sec_msg_markup = false);
/// Show an informational dialog
void gui_show_info_dialog(const std::string& message, Gtk::Window* parent = 0);
/// Show an informational dialog with a (possibly markupped) secondary message
void gui_show_info_dialog(const std::string& message, const std::string& sec_message,
Gtk::Window* parent = 0, bool sec_msg_markup = false);
/// Show a text entry dialog. \c result is filled with the user-entered string on success.
/// \return false if Cancel was clicked.
// Returns false if Cancel was clicked. Puts the user-entered string into result otherwise.
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message,
std::string& result, const std::string& default_str, Gtk::Window* parent = 0);
/// Show a text entry dialog with a (possibly markupped) secondary message.
/// \c result is filled with the user-entered string on success.
/// \return false if Cancel was clicked.
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, const std::string& sec_message,
std::string& result, const std::string& default_str, Gtk::Window* parent = 0, bool sec_msg_markup = false);
@@ -62,5 +47,3 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes
#endif
/// @}
+11 -16
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <algorithm> // std::max, std::min
#include <cmath> // std::floor
@@ -26,7 +21,7 @@ int64_t SelfTest::get_remaining_seconds() const
if (total <= 0)
return -1; // unknown
double gran = (double(total) / 9.); // seconds per 10%
double gran = (total / 9.); // seconds per 10%
// since remaining_percent_ may be manually set to 100, we limit from the above.
double rem_seconds_at_last_change = std::min(double(total), gran * remaining_percent_ / 10.);
double rem = rem_seconds_at_last_change - timer_.elapsed();
@@ -108,7 +103,7 @@ std::string SelfTest::start(hz::intrusive_ptr<CmdexSync> smartctl_ex)
return "Invalid test specified";
std::string output;
std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-t " + test_param, smartctl_ex, output); // --test=
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -168,7 +163,7 @@ std::string SelfTest::force_stop(hz::intrusive_ptr<CmdexSync> smartctl_ex)
// To abort non-captive short, long and conveyance tests, use "--abort".
std::string output;
std::string error_msg = drive_->execute_device_smartctl("--abort", smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-X", smartctl_ex, output); // --abort
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -207,15 +202,14 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
return "Invalid drive given.";
std::string output;
// std::string error_msg = drive_->execute_device_smartctl("--log=selftest", smartctl_ex, output);
std::string error_msg = drive_->execute_device_smartctl("--capabilities", smartctl_ex, output);
// std::string error_msg = drive_->execute_smartctl("-l selftest", smartctl_ex, output); // --log=
std::string error_msg = drive_->execute_smartctl("-c", smartctl_ex, output); // --capabilities
if (!error_msg.empty()) // checks for empty output too
return error_msg;
StorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD;
SmartctlParser ps;
if (!ps.parse_full(output, disk_type)) { // try to parse it
if (!ps.parse_full(output)) { // try to parse it
return ps.get_error_msg();
}
@@ -257,8 +251,7 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
poll_in_seconds_ = 30; // just a guess
} else {
// seconds per 10%. use double, because e.g. 60sec test gives silly values with int.
double gran = (double(total) / 9.);
double gran = (total / 9.); // seconds per 10%. use double, because e.g. 60sec test gives silly values with int.
// Add 1/10 for disk load delays, etc... . Limit to 15sec, in case of very quick tests.
poll_in_seconds_ = std::max(int64_t(15), int64_t(gran / 3. + (gran / 10.)));
@@ -290,4 +283,6 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
/// @}
+28 -46
View File
@@ -1,21 +1,16 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SELFTEST_H
#define SELFTEST_H
#include <glibmm.h>
#include <string>
#include <stdint.h>
#include <glibmm/timer.h>
#include "hz/cstdint.h"
#include "hz/intrusive_ptr.h"
#include "storage_device.h"
@@ -23,20 +18,16 @@
/// SMART self-test runner.
class SelfTest : public hz::intrusive_ptr_referenced {
public:
/// Test type
enum test_t {
type_ioffline, ///< Immediate offline, not supported
type_short, ///< Short self-test
type_long, ///< Extended (a.k.a. long) self-test
type_conveyance ///< Conveyance self-test
type_ioffline, // immediate offline, not supported yet
type_short,
type_long,
type_conveyance
};
/// Get displayable name for a test type
static std::string get_test_name(test_t t)
{
switch (t) {
@@ -49,7 +40,7 @@ class SelfTest : public hz::intrusive_ptr_referenced {
}
/// Constructor. \c drive must have the capabilities present in its properties.
// The drive must have the capabilities present in properties.
SelfTest(StorageDeviceRefPtr drive, test_t type)
: drive_(drive), type_(type)
{
@@ -57,8 +48,7 @@ class SelfTest : public hz::intrusive_ptr_referenced {
}
/// Clear results of previous test
void clear()
void clear() // clear results of previous test
{
status_ = StorageSelftestEntry::status_unknown;
remaining_percent_ = -1;
@@ -68,94 +58,86 @@ class SelfTest : public hz::intrusive_ptr_referenced {
}
/// Check if the test is currently active
bool is_active() const
{
return (status_ == StorageSelftestEntry::status_in_progress);
}
/// Get remaining time percent until the test completion.
/// \return -1 if N/A or unknown.
// returns -1 if n/a or unknown.
int8_t get_remaining_percent() const
{
return remaining_percent_;
}
/// Get estimated time of completion for the test.
/// \return -1 if N/A or unknown. Note that 0 is a valid value.
// Returns estimated time of completion for the test. returns -1 if n/a or unknown. 0 is a valid value.
int64_t get_remaining_seconds() const;
/// Get test type
test_t get_test_type() const
{
return type_;
}
/// Get test status
StorageSelftestEntry::status_t get_status() const
{
return status_;
}
/// Get the number of seconds after which the caller should call update().
// get the number of seconds after which the caller should call update().
int64_t get_poll_in_seconds() const
{
return poll_in_seconds_;
}
/// Get a constant "test duration during idle" capability drive's stored capabilities. -1 if N/A.
// gets a constant "test duration during idle" capability drive's stored capabilities. -1 if N/A.
int64_t get_min_duration_seconds() const;
/// Gets the current test type support status from drive's stored capabilities.
// gets the current test type support status from drive's stored capabilities.
bool is_supported() const;
/// Start the test.
/// \return error message on error, empty string on success.
// These return an error message, or empty string if no error.
// start the test
std::string start(hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
/// Abort the running test.
/// \return error message on error, empty string on success.
// abort the running test
std::string force_stop(hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
/// Update status variables. The user should call this every get_poll_in_seconds() seconds.
/// \return error message on error, empty string on success.
// update status variables
std::string update(hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
private:
StorageDeviceRefPtr drive_;
StorageDeviceRefPtr drive_; ///< Drive to run the tests on
test_t type_; ///< Test type
test_t type_;
// status variables:
StorageSelftestEntry::status_t status_; ///< Current status of the test as reported by the drive
int8_t remaining_percent_; ///< Remaining %. 0 means unknown, -1 means N/A. This is set to 100 on start.
int8_t last_seen_percent_; ///< Last reported %, to detect changes in percentage (needed for timer update).
mutable int64_t total_duration_; ///< Total duration needed for the test, as reported by the drive. Constant. This variable acts as a cache.
int64_t poll_in_seconds_; ///< The user is asked to poll after this much seconds have passed.
StorageSelftestEntry::status_t status_;
int8_t remaining_percent_; // 0 means unknown, -1 means n/a. Set to 100 on start.
int8_t last_seen_percent_; // to detect changes in percentage (needed for timer update)
mutable int64_t total_duration_; // total duration needed for the test, as reported by drive. constant. this is a cache.
int64_t poll_in_seconds_; // the user is asked to poll after this much seconds have passed.
Glib::Timer timer_; ///< Counts time since the last percent change
Glib::Timer timer_; // counts time since the last percent change
};
/// A reference-counting pointer to SelfTest
typedef hz::intrusive_ptr<SelfTest> SelfTestPtr;
#endif
/// @}
+5 -86
View File
@@ -1,26 +1,18 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <glibmm.h> // Glib::shell_quote()
#include "smartctl_executor.h"
#include "hz/fs_path.h"
#include "hz/win32_tools.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
// returns an empty string if not found.
std::string get_smartctl_binary()
{
std::string smartctl_binary;
@@ -34,20 +26,16 @@ std::string get_smartctl_binary()
if (!use_smt)
break;
std::string smt_regpath, smt_regpath_wow, smt_regkey, smt_smartctl;
std::string smt_regpath, smt_regkey, smt_smartctl;
rconfig::get_data("system/win32_smartmontools_regpath", smt_regpath);
rconfig::get_data("system/win32_smartmontools_regpath_wow", smt_regpath_wow); // same as above, but with WOW6432Node
rconfig::get_data("system/win32_smartmontools_regkey", smt_regkey);
rconfig::get_data("system/win32_smartmontools_smartctl_binary", smt_smartctl);
if ((smt_regpath.empty() && smt_regpath_wow.empty()) || smt_regkey.empty() || smt_smartctl.empty())
if (smt_regpath.empty() || smt_regkey.empty() || smt_smartctl.empty())
break;
std::string smt_inst_dir;
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, smt_regpath, smt_regkey, smt_inst_dir);
if (smt_inst_dir.empty()) {
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, smt_regpath_wow, smt_regkey, smt_inst_dir);
}
hz::win32_get_registry_value_string(smt_inst_dir, HKEY_LOCAL_MACHINE, smt_regpath, smt_regkey);
if (smt_inst_dir.empty()) {
debug_out_info("app", DBG_FUNC_MSG << "Smartmontools installation not found in \"HKLM\\"
@@ -74,72 +62,3 @@ std::string get_smartctl_binary()
std::string execute_smartctl(const std::string& device, const std::string& device_opts,
const std::string& command_options,
hz::intrusive_ptr<CmdexSync> smartctl_ex, std::string& smartctl_output)
{
#ifndef _WIN32 // win32 doesn't have slashes in devices names
{
std::string::size_type pos = device.rfind('/'); // find basename
if (pos == std::string::npos) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n");
return "Invalid device name specified.";
}
}
#endif
if (!smartctl_ex) // if it doesn't exist, create a default one
smartctl_ex = new SmartctlExecutor(); // will be auto-deleted
std::string smartctl_binary = get_smartctl_binary();
if (smartctl_binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n");
return "Smartctl binary is not specified in configuration.";
}
std::string smartctl_def_options;
rconfig::get_data("system/smartctl_options", smartctl_def_options);
if (!smartctl_def_options.empty())
smartctl_def_options += " ";
std::string device_specific_options = device_opts;
if (!device_specific_options.empty())
device_specific_options += " ";
smartctl_ex->set_command(Glib::shell_quote(smartctl_binary),
smartctl_def_options + device_specific_options + command_options
+ " " + Glib::shell_quote(device));
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n");
smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
// check if it's a device permission error.
// Smartctl open device: /dev/sdb failed: Permission denied
if (app_pcre_match("/Smartctl open device.+Permission denied/mi", smartctl_output)) {
return "Permission denied while opening device.";
}
// smartctl_output = smartctl_ex->get_stdout_str();
return smartctl_ex->get_error_msg();
}
// any_to_unix is needed for windows
smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (smartctl_output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
}
return std::string();
}
/// @}
+20 -46
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SMARTCTL_EXECUTOR_H
#define SMARTCTL_EXECUTOR_H
@@ -17,12 +12,11 @@
/// Smartctl executor template.
template<class ExecutorSync>
class SmartctlExecutorGeneric : public ExecutorSync {
public:
/// Constructor
SmartctlExecutorGeneric(const std::string& cmd, const std::string& cmdargs)
: ExecutorSync(cmd, cmdargs)
{
@@ -30,14 +24,12 @@ class SmartctlExecutorGeneric : public ExecutorSync {
}
/// Constructor
SmartctlExecutorGeneric()
{
this->construct();
}
/// Virtual destructor
virtual ~SmartctlExecutorGeneric()
{ }
@@ -45,27 +37,25 @@ class SmartctlExecutorGeneric : public ExecutorSync {
protected:
/// Called by constructors
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status, NULL);
ExecutorSync::cmdex_.set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status, NULL);
this->set_error_header("An error occurred while executing smartctl:\n\n");
}
enum {
exit_cant_parse = 1 << 0, ///< Smartctl error code bit
exit_open_failed = 1 << 1, ///< Smartctl error code bit
exit_smart_failed = 1 << 2, ///< Smartctl error code bit
exit_disk_failing = 1 << 3, ///< Smartctl error code bit
exit_prefail_threshold = 1 << 4, ///< Smartctl error code bit
exit_threshold_in_past = 1 << 5, ///< Smartctl error code bit
exit_error_log = 1 << 6, ///< Smartctl error code bit
exit_self_test_log = 1 << 7 ///< Smartctl error code bit
exit_cant_parse = 1 << 0,
exit_open_failed = 1 << 1,
exit_smart_failed = 1 << 2,
exit_disk_failing = 1 << 3,
exit_prefail_threshold = 1 << 4,
exit_threshold_in_past = 1 << 5,
exit_error_log = 1 << 6,
exit_self_test_log = 1 << 7
};
/// Translate smartctl error code to a readable message
static std::string translate_exit_status(int status, void* user_data)
{
const char* table[] = {
@@ -94,7 +84,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
}
/// Import the last error from command executor and clear all errors there
// import the last error from cmdex_ and clear all errors there
virtual void import_error()
{
Cmdex& cmdex = this->get_command_executor();
@@ -124,8 +114,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
/// This is called when an error occurs in command executor.
/// Note: The warnings are already printed via debug_* in cmdex.
// The warnings are already printed via debug_* in cmdex.
virtual void on_error_warn(hz::ErrorBase* e)
{
if (!e)
@@ -139,15 +128,11 @@ class SmartctlExecutorGeneric : public ExecutorSync {
if (error_type == "exit") {
int exit_code = 0;
e->get_code(exit_code);
// Ignore everyone except this.
// Note that we don't treat exit_open_failed as failure because:
// * It may be returned from a DVD that returns product info but has no disk inside;
// * It may be returned from a usb flash drive with -d scsi;
// * exit_cant_parse is returned when opening unsupported usb drives without -d scsi.
if ( !(exit_code & exit_cant_parse) )
// ignore everyone except these
if ( !((exit_code & exit_cant_parse) || (exit_code & exit_open_failed)) )
return;
// ignore giochannel errors - higher level errors will be triggered, and they more user-friendly.
// ignore giochannel errors - higher level errors will be triggered, and they more user-friendly.
} else if (error_type == "giochannel" || error_type == "custom") {
return;
}
@@ -155,33 +140,22 @@ class SmartctlExecutorGeneric : public ExecutorSync {
this->set_error_msg(e->get_message());
}
};
/// Smartctl executor without GUI support
typedef SmartctlExecutorGeneric<CmdexSync> SmartctlExecutor;
/// A reference-counting pointer to SmartctlExecutor
typedef hz::intrusive_ptr<SmartctlExecutor> SmartctlExecutorRefPtr;
/// Get smartctl binary (from config, etc...). Returns an empty string if not found.
// returns an empty string if not found.
std::string get_smartctl_binary();
/// Execute smartctl on device \c device.
/// \return error message on error, empty string on success.
std::string execute_smartctl(const std::string& device, const std::string& device_opts,
const std::string& command_options,
hz::intrusive_ptr<CmdexSync> smartctl_ex, std::string& smartctl_output);
#endif
/// @}
+1 -10
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SMARTCTL_EXECUTOR_GUI_H
#define SMARTCTL_EXECUTOR_GUI_H
@@ -19,16 +14,12 @@
/// Smartctl executor with GUI support
typedef SmartctlExecutorGeneric<CmdexSyncGui> SmartctlExecutorGui;
/// A reference-counting pointer to SmartctlExecutor
typedef hz::intrusive_ptr<SmartctlExecutorGui> SmartctlExecutorGuiRefPtr;
#endif
/// @}
+4 -12
View File
@@ -1,26 +1,20 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib_tests
/// \weakgroup applib_tests
/// @{
#include <iostream>
#include <gtkmm.h>
#include <gtkmm/main.h>
// #include "smartctl_executor_gui.h"
#include "smartctl_executor.h"
/// Main function of the test
int main(int argc, char** argv)
{
// Glib::thread_init();
Glib::thread_init();
Gtk::Main m(argc, argv);
// NOTE: Don't use long options (e.g. --info). Use short ones (e.g. -i),
@@ -57,5 +51,3 @@ int main(int argc, char** argv)
/// @}
File diff suppressed because it is too large Load Diff
+30 -53
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef SMARTCTL_PARSER_H
#define SMARTCTL_PARSER_H
@@ -19,73 +14,63 @@
/// Smartctl parser.
/// Note: ALL parse_* functions (except parse_full() and parse_version())
/// expect data in unix-newline format!
class SmartctlParser {
public:
/// Property list
typedef std::vector<StorageProperty> prop_list_t;
/// Constructor
SmartctlParser();
// Note: ALL parse_* functions (except parse_full() and parse_version())
// expect data in unix-newline format!
/// Parse full "smartctl -x" output
bool parse_full(const std::string& s, StorageAttribute::DiskType disk_type);
// Parse full "smartctl -a" output
bool parse_full(const std::string& s);
/// Supply any output of smartctl here, the smartctl version will be retrieved.
// supply any output of smartctl here.
static bool parse_version(const std::string& s, std::string& version, std::string& version_full);
/// Check that the version of smartctl output can be parsed with this parser.
static bool check_parsed_version(const std::string& version_str, const std::string& version_full_str);
// check that the version of smartctl output can be parsed with this parser.
static bool check_version(const std::string& version_str, const std::string& version_full_str);
/// Convert e.g. "1,000,204,886,016 bytes" to 1.00 TiB [931.51 GB, 1000204886016 bytes]
// convert e.g. "1,000,204,886,016 bytes" to 1.00 TiB [931.51 GB, 1000204886016 bytes]
static std::string parse_byte_size(const std::string& str, uint64_t& bytes, bool extended);
// You don't really need to call these functions, use the ones above.
/// Parse the section part (with "=== .... ===" header) - info or data sections.
// Parse the section part (with "=== .... ===" header) - info or data sections.
bool parse_section(const std::string& header, const std::string& body);
/// Parse the info section (without "===" header).
/// This includes --info and --get=all.
// Parse the info section (without "===" header)
bool parse_section_info(const std::string& body);
/// Parse a component (one line) of the info section
// Parse a component (one line) of the info section
bool parse_section_info_property(StorageProperty& p);
/// Parse the Data section (without "===" header)
// Parse the Data section (without "===" header)
bool parse_section_data(const std::string& body);
/// Parse subsections of Data section
// Parse subsections of Data section
bool parse_section_data_subsection_health(const std::string& sub);
bool parse_section_data_subsection_capabilities(const std::string& sub);
bool parse_section_data_subsection_attributes(const std::string& sub);
bool parse_section_data_subsection_directory_log(const std::string& sub);
bool parse_section_data_subsection_error_log(const std::string& sub);
bool parse_section_data_subsection_selftest_log(const std::string& sub);
bool parse_section_data_subsection_selective_selftest_log(const std::string& sub);
bool parse_section_data_subsection_scttemp_log(const std::string& sub);
bool parse_section_data_subsection_scterc_log(const std::string& sub);
bool parse_section_data_subsection_devstat(const std::string& sub);
bool parse_section_data_subsection_sataphy(const std::string& sub);
/// Check the capabilities for internal properties we can use.
// Check the capabilities for internal properties we can use.
bool parse_section_data_internal_capabilities(StorageProperty& cap);
/// Clear parsed data
void clear()
{
data_full_.clear();
@@ -97,12 +82,10 @@ class SmartctlParser {
}
/// Get "full" data, as passed to parse_full().
std::string get_data_full() const
{
return data_full_;
}
/*
std::string get_data_section_info() const
{
@@ -114,16 +97,12 @@ class SmartctlParser {
return data_section_data_;
}
*/
/// Get parse error message. Call this only if parsing doesn't succeed,
/// to get a friendly error message.
std::string get_error_msg() const
{
return "Cannot parse smartctl output: " + error_msg_;
}
/// Get parse result properties
const prop_list_t& get_properties() const
{
return properties_;
@@ -134,32 +113,26 @@ class SmartctlParser {
private:
/// Add a property into property list, look up and set its description
// adds a property into property list, looks up and sets its description
void add_property(StorageProperty p);
/// Set "full" data ("smartctl -x" output)
void set_data_full(const std::string& s)
{
data_full_ = s;
}
/// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -x" output)
void set_data_section_info(const std::string& s)
{
data_section_info_ = s;
}
/// Parse "data" section data (the second part of "smartctl -x" output).
void set_data_section_data(const std::string& s)
{
data_section_data_ = s;
}
/// Set error message
void set_error_msg(const std::string& s)
{
error_msg_ = s;
@@ -167,15 +140,15 @@ class SmartctlParser {
prop_list_t properties_; ///< Parsed data properties
prop_list_t properties_;
std::string data_full_; ///< full data, filled by parse_full()
std::string data_section_info_; ///< "info" section data, filled by parse_section_info()
std::string data_section_data_; ///< "data" section data, filled by parse_section_data()
// These are filled by the appropriate parse_* functions
std::string data_full_;
std::string data_section_info_;
std::string data_section_data_;
std::string error_msg_; ///< This will be filled with some displayable message on error
std::string error_msg_; // on error this will be filled with displayable message
StorageAttribute::DiskType disk_type_; ///< Disk type (HDD, SSD)
};
@@ -184,6 +157,10 @@ class SmartctlParser {
#endif
/// @}
#endif
+4 -14
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib_tests
/// \weakgroup applib_tests
/// @{
#undef HZ_USE_LIBDEBUG
#define HZ_USE_LIBDEBUG 0
@@ -18,14 +13,13 @@
#include <iostream>
#include <cstdlib>
#include "libdebug/libdebug.h"
#include "hz/debug.h"
#include "hz/fs_file.h"
#include "storage_property.h"
#include "smartctl_parser.h"
/// Main function of the test
int main(int argc, char** argv)
{
if (argc < 2) {
@@ -33,8 +27,6 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
debug_register_domain("app");
std::string file_str = argv[1];
hz::File file(file_str);
@@ -47,7 +39,7 @@ int main(int argc, char** argv)
SmartctlParser sp;
if (!sp.parse_full(contents, StorageAttribute::DiskAny)) {
if (!sp.parse_full(contents)) {
debug_out_error("app", "Cannot parse file contents: " << sp.get_error_msg() << "\n");
return EXIT_FAILURE;
}
@@ -86,5 +78,3 @@ int main()
/// @}
+6 -14
View File
@@ -1,23 +1,17 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib_tests
/// \weakgroup applib_tests
/// @{
#include <iostream>
#include <glib.h>
/// Main function of the test
int main(int argc, char** argv)
{
// g_thread_init(NULL);
g_thread_init(NULL);
GPid pid;
int fd_stdout = 0, fd_stderr = 0;
@@ -57,17 +51,17 @@ int main(int argc, char** argv)
#endif
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
int channel_flags = ~G_IO_FLAG_NONBLOCK;
GIOFlags flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
if (channel_stdout) {
g_io_channel_set_encoding(channel_stdout, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stdout, GIOFlags(g_io_channel_get_flags(channel_stdout) & channel_flags), 0);
g_io_channel_set_flags(channel_stdout, flags, 0);
g_io_channel_set_buffer_size(channel_stdout, 10000);
}
if (channel_stderr) {
g_io_channel_set_encoding(channel_stderr, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stderr, GIOFlags(g_io_channel_get_flags(channel_stderr) & channel_flags), 0);
g_io_channel_set_flags(channel_stderr, flags, 0);
g_io_channel_set_buffer_size(channel_stderr, 10000);
}
@@ -82,5 +76,3 @@ int main(int argc, char** argv)
/// @}
+580 -42
View File
@@ -1,62 +1,604 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <algorithm>
#include "hz/hz_config.h" // CONFIG_*
#include <ios> // std::boolalpha
#include <algorithm> // std::find
#include <cstdio> // std::fgets()
#include <cerrno> // ENXIO
#if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
#include <util.h> // getrawpartition()
#endif
#if defined CONFIG_KERNEL_WIN32
#include <windows.h> // CreateFile(), CloseHandle(), etc...
#endif
#include "hz/debug.h"
#include "hz/fs_dir.h"
#include "hz/fs_file.h"
#include "hz/string_sprintf.h"
#include "hz/local_algo.h" // hz::shell_sort()
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "smartctl_executor.h"
#include "storage_detector.h"
#include "storage_detector_linux.h"
#include "storage_detector_win32.h"
#include "storage_detector_other.h"
namespace {
// Note: Since 3ware RAID controllers need additional flags on most (all?) OSes,
// and they may ignore the actual devices names, I have no idea how to support them.
#if defined CONFIG_KERNEL_LINUX
// Linux 2.6 with udev. Scan /dev/disk/by-id - the directory entries there
// are symlinks to respective /dev devices. Some devices have multiple
// links pointing to them, so unique filter is needed.
/*
Sample listing:
------------------------------------------------------------
# ls -1 /dev/disk/by-id
ata-ST31000340AS_9QJ0FFG7
ata-ST31000340AS_9QJ0FFG7-part1
ata-ST31000340AS_9QJ0FFG7-part2
ata-ST3500630AS_9QG0R38D
ata-ST3500630AS_9QG0R38D-part1
scsi-SATA_ST31000340AS_9QJ0FFG7
scsi-SATA_ST31000340AS_9QJ0FFG7-part1
scsi-SATA_ST31000340AS_9QJ0FFG7-part2
scsi-SATA_ST3500630AS_9QG0R38D
scsi-SATA_ST3500630AS_9QG0R38D-part1
*/
inline std::string detect_drives_linux_udev_byid(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through device scan directory /dev/disk/by-id...\n");
std::string dev_dir; // this defaults to "/dev/disk/by-id"
if (!rconfig::get_data("system/linux_udev_byid_path", dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory path is not set.\n");
return "Device scan directory path is not set.";
}
hz::Dir dir(dev_dir);
std::vector<std::string> all_devices;
if (!dir.entry_list(all_devices, false)) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
}
return error_msg;
}
std::vector<std::string> blacklist;
blacklist.push_back("/-part[0-9]+$/");
// filter-out the ones with "partN" in them
for (unsigned int i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
blacked = true;
break;
}
}
if (blacked)
continue;
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// those are usually relative links, so find out where they are pointing to.
std::string link_dest;
if (path.get_link_destination(link_dest)) {
path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
path.compress(); // compress in case there are ../-s.
}
// skip number-ended devices, in case we couldn't filter out the scan directory correctly
// (it's user-settable after all).
// if (app_pcre_match("/[0-9]+$/", path.str()))
// continue;
if (std::find(devices.begin(), devices.end(), path.str()) == devices.end()) // there may be duplicates
devices.push_back(path.str());
}
return std::string();
}
// Procfs files don't support SEEK_END or ftello() (I think). Anyway, they can't
// be read through hz::File::get_contents, so use this function instead.
inline bool read_proc_partitions_file(hz::File& file, std::vector<std::string>& lines)
{
if (!file.open("rb")) // closed automatically
return false; // the error message is in File itself.
FILE* fp = file.get_handle();
if (!fp)
return false;
char line[256];
while (std::fgets(line, sizeof(line), fp) != NULL) {
if (*line != '\0')
lines.push_back(line);
}
return true;
}
// Linux (tested with 2.4 and 2.6) /proc/partitions. Parses the file, appends /dev to each entry.
// Note that file format changed from 2.4 to 2.6 (some statistics fields moved to another file).
// No /proc/partitions on at least freebsd, solaris and osx, afaik.
/*
Sample 1 (2.4, devfs, with statistics):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
3 0 60051600 ide/host0/bus0/target0/lun0/disc 72159 136746 1668720 467190 383039 658435 8342136 1659840 -429 17038808 22703194
3 1 1638598 ide/host0/bus0/target0/lun0/part1 0 0 0 0 0 0 0 0 0 0 0
3 2 1028160 ide/host0/bus0/target0/lun0/part2 0 0 0 0 0 0 0 0 0 0 0
3 3 3092512 ide/host0/bus0/target0/lun0/part3 0 0 0 0 0 0 0 0 0 0 0
3 4 1 ide/host0/bus0/target0/lun0/part4 0 0 0 0 0 0 0 0 0 0 0
3 5 514048 ide/host0/bus0/target0/lun0/part5 1458 7877 74680 9070 2140 18285 166272 42490 0 10100 52000
------------------------------------------------------------
Sample 2 (2.6):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
1 0 4096 ram0
8 0 156290904 sda
8 1 39070048 sda1
8 2 7560 sda2
8 3 1 sda3
------------------------------------------------------------
Sample 3 (2.6 on nokia n8xx, spaces may be missing):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
31 0 128 mtdblock0
31 1 384 mtdblock1
31 2 2048 mtdblock2
31 3 2048 mtdblock3
31 4 257536 mtdblock4
254 0 3932160 mmcblk0
254 1 3928064 mmcblk0p1
254 8 1966080 mmcblk1
254 9 2007032 mmcblk1p1
*/
inline std::string detect_drives_linux_proc_partitions(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /proc/partitions...\n");
std::string parts_file;
if (!rconfig::get_data("system/linux_proc_partitions_path", parts_file) || parts_file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n");
return "Partitions file path is not set.";
}
hz::File file(parts_file);
// std::string contents;
std::vector<std::string> lines;
// if (!f.get_contents(contents)) {
if (!read_proc_partitions_file(file, lines)) { // this outputs to debug too
std::string error_msg = file.get_error_utf8(); // save before calling other file functions
if (!file.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n");
}
return error_msg;
}
// debug_out_dump("app", DBG_FUNC_MSG << "Dumping partitions file:\n" << contents << "\n");
// hz::string_split(contents, '\n', lines, true);
std::vector<std::string> blacklist;
// fixme: not sure about how partitions are visible with twa0.
blacklist.push_back("/d[a-z][0-9]+$/"); // sda1, hdb2 - partitions. twa0 and twe1 are drives, not partitions.
blacklist.push_back("/ram[0-9]+$/"); // ramdisks?
blacklist.push_back("/loop[0-9]*$/"); // not sure if loop devices go there, but anyway...
blacklist.push_back("/part[0-9]+$/"); // devfs had them
blacklist.push_back("/p[0-9]+$/"); // partitions are usually marked this way
blacklist.push_back("/md[0-9]*$/"); // linux software raid
for (unsigned int i = 0; i < lines.size(); ++i) {
std::string line = hz::string_trim_copy(lines[i]);
if (line.empty() || app_pcre_match("/^major/", line)) // file header
continue;
std::string dev;
if (!app_pcre_match("/^[ \\t]*[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+([^ \\t\\n]+)/", line, &dev) || dev.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse line \"" << line << "\".\n");
continue;
}
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, dev)) {
blacked = true;
break;
}
}
if (blacked)
continue;
std::string path = "/dev/" + dev; // let's just hope the it's /dev.
if (std::find(devices.begin(), devices.end(), path) == devices.end()) // there may be duplicates
devices.push_back(path);
}
return std::string();
}
#elif defined CONFIG_KERNEL_WINDOWS
// smartctl accepts various variants, the most straight being pdN,
// (or /dev/pdN, /dev/ being optional) where N comes from
// "\\.\PhysicalDriveN" (winnt only).
inline std::string detect_drives_win32(std::vector<std::string>& devices)
{
for (int drive_num = 0; ; ++drive_num) {
std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num);
// If the drive is openable, then it's there.
// NOTE: Administrative privileges are required to open it.
// Yes, CreateFile() is open, not create. Yes, it's silly (ah, win32...).
HANDLE h = CreateFile(name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
// The numbers seem to be consecutive, so break on first invalid.
if (h == INVALID_HANDLE_VALUE)
break;
CloseHandle(h);
devices.push_back(hz::string_sprintf("pd%d", drive_num));
}
return std::string();
}
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
#else // FreeBSD, Solaris, etc... .
inline std::string detect_drives_dev(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /dev...\n");
std::string sdev_config_path;
#ifdef CONFIG_KERNEL_SOLARIS
sdev_config_path = "system/solaris_dev_path";
#else // other unixes
sdev_config_path = "system/unix_sdev_path";
#endif
std::string dev_dir; // defaults to /dev for freebsd, /dev/rdsk for solaris.
if (!rconfig::get_data(sdev_config_path, dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n");
return "Device directory path is not set.";
}
hz::Dir dir(dev_dir);
std::vector<std::string> all_devices;
if (!dir.entry_list(all_devices, false, hz::DirSortNone())) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
}
return error_msg;
}
// platform whitelist
std::vector<std::string> whitelist;
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY
// FreeBSD example device names:
// * /dev/ad0 - ide disk 0. /dev/ad0a - non-slice dos partition 1.
// * /dev/da1s1e - scsi disk 1 (second disk), slice 1 (aka dos partition 1),
// bsd partition e (multiple bsd partitions may be inside one dos partition).
// * /dev/acd0 - first cdrom. /dev/acd0c - ?.
// Info: http://www.freebsd.org/doc/en/books/handbook/disks-naming.html
// Of two machines I had access to, freebsd 6.3 had only real devices,
// while freebsd 4.10 had lots of dummy ones (ad1, ad2, ad3, da0, da1, da2, da3, sa0, ...).
whitelist.push_back("/^ad[0-9]+$/"); // adN without suffix - fbsd ide
whitelist.push_back("/^da[0-9]+$/"); // daN without suffix - fbsd scsi, usb
// whitelist.push_back("/ ^sa[0-9]+$/"); // saN without suffix - fbsd scsi tape
// whitelist.push_back("/^ast[0-9]+$/"); // astN without suffix - fbsd ide tape
whitelist.push_back("/^aacd[0-9]+$/"); // fbsd adaptec raid
whitelist.push_back("/^mlxd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^mlyd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^amrd[0-9]+$/"); // fbsd AMI raid
whitelist.push_back("/^idad[0-9]+$/"); // fbsd compaq raid
whitelist.push_back("/^twed[0-9]+$/"); // fbsd 3ware raid
// these are checked by smartctl, but they are not mentioned in freebsd docs:
whitelist.push_back("/^tw[ae][0-9]+$/"); // fbsd 3ware raid
// unused: acd - ide cdrom, cd - scsi cdrom, mcd - mitsumi cdrom, scd - sony cdrom,
// fd - floppy, fla - diskonchip flash.
#elif defined CONFIG_KERNEL_SOLARIS
// /dev/rdsk contains "raw" physical char devices, as opposed to
// "filesystem" block devices in /dev/dsk. smartctl needs rdsk.
// x86:
// /dev/rdsk/c0t0d0p0:1
// Where c0 is the controller number.
// t0 is the target (SCSI ID number) (omit for ATAPI)
// d0 is always 0 for SCSI, the drive # for ATAPI
// p0 is the partition (p0 is the entire disk, or p1 - p4)
// :1 is the logical drive (c - z or 1 - 24) (that is, logical partitions inside extended partition)
// /dev/rdsk/c0d0p2:1
// where p2 means the extended partition (type 0x05) is partition 2 (out of 1-4) and
// ":1" is the 2nd extended partition (:0 would be the first extended partition). -- not sure about this!
// p0 whole physical disk
// p1 - p4 Four primary fdisk partitions
// p5 - p30 26 logical drives in extended DOS partition (not implemented by Solaris)
// s0 - s15 16 slices in the Solaris FDISK partition (SPARC has only 8)
// On Solaris, by convention, s2 is the whole Solaris partition.
// SPARC: There are no *p* devices on sparc afaik, only slices. By convention,
// s2 is used as a "whole" disk there (but not with EFI partitions!).
// So, c0d0s2 is the whole disk there.
// x86 has both c0d0s2 and c0d0p2, where s2 is a whole solaris partition.
// NOTE: It seems that smartctl searches for s0 (aka root partition) in the end. I'm not sure
// what implications this has for x86, but we replicate this behaviour.
// Note: Cdroms are ATAPI, so they pose as SCSI, as opposed to IDE hds.
// TODO: No idea how to implement /dev/rmt (scsi tape devices),
// I have no files in that directory.
whitelist.push_back("/^c[0-9]+(?:t[0-9]+)?d[0-9]+s0$/");
#elif defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// OpenBSD / NetBSD have /dev/wdNc for IDE/ATA, /dev/sdNc for SCSI disk,
// /dev/stNc for scsi tape. N is [0-9]+. "c" means "whole disk" (not sure about
// different architectures though). There are no "sdN" devices, only "sdNP".
// Another manual says that wd0d would be a whole disk, while wd0c is its
// bsd part only (on x86) (only on netbsd?). Anyway, getrawpartition() gives
// us the letter we need.
// There is no additional level of names for BSD subpartitions (unlike freebsd).
// cd0a is cdrom.
// Dummy devices are present.
// Note: This detection may take a while (probably due to open() check).
char whole_part = 'a' + getrawpartition(); // from bsd's util.h
whitelist.push_back(hz::string_sprintf("/^wd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^sd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^st[0-9]+%c$/", whole_part));
#elif defined CONFIG_KERNEL_DARWIN
// Darwin has /dev/disk0, /dev/disk0s1, etc...
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^disk[0-9]+$/");
#elif defined CONFIG_KERNEL_QNX
// QNX has /dev/hd0, /dev/hd0t78 (partition?).
// Afaik, IDE and SCSI have the same prefix. fd for floppy, cd for cdrom.
// Not sure about the tapes.
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^hd[0-9]+$/");
#endif // unix platforms
std::vector<std::string> matched_devices;
for (unsigned int i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
bool matched = false;
for (std::vector<std::string>::const_iterator iter = whitelist.begin(); iter != whitelist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
matched = true;
break;
}
}
if (!matched)
continue;
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// In case these are links, trace them to originals.
// We don't replace /dev files with real devices - it leads to really bad paths (pci ids for solaris, etc...).
std::string link_dest;
hz::FsPath real_dev_path;
if (path.get_link_destination(link_dest)) {
real_dev_path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
real_dev_path.compress(); // compress in case there are ../-s.
// solaris has dangling links for non-existant devices, so filter them out.
if (!real_dev_path.exists())
continue;
}
matched_devices.push_back(path.str());
}
// List ones who need dummy device filtering
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY \
|| defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// Since we may have encountered dummy devices, we should check
// if they really exist. Unfortunately, this involves opening them, which
// is not good with some media (e.g. it may hang on audio cd (freebsd, maybe others), etc...).
// That's why we don't whitelist cd devices. Let's hope usb and others are
// ok with this. Dummy devices give errno ENXIO (6, Device not configured) on *bsd.
// In Linux ENXIO is (6, No such device or address).
// Don't do this on solaris - we can't distinguish between cdroms and hds there.
// If there are less than 4 devices, they are probably not dummy (newer freebsd).
bool open_needed = (matched_devices.size() >= 4);
if (open_needed) {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", will try to filter non-existent ones out.\n");
} else {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", no need for filtering them out.\n");
}
for (unsigned int i = 0; i < matched_devices.size(); ++i) {
hz::File dev_file(matched_devices[i]);
// we check errno because OS may deny us for some other reason on valid devices.
if (open_needed && !dev_file.open("rb") && dev_file.get_errno() == ENXIO) {
debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" failed to open, ignoring.\n");
continue;
}
if (open_needed)
debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" opened successfully, adding to device list.\n");
devices.push_back(dev_file.str());
}
#else // not *BSD
for (unsigned int i = 0; i < matched_devices.size(); ++i) {
devices.push_back(matched_devices[i]);
}
#endif
hz::shell_sort(devices.begin(), devices.end());
return std::string();
}
#endif // platforms
} // anon. ns
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
{
debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n");
std::vector<StorageDeviceRefPtr> all_detected;
std::vector<std::string> devices;
std::string error_msg;
bool found = false;
// Try each one and move to next if it fails.
#if defined CONFIG_KERNEL_LINUX
error_msg = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback.
// Disable by-id detection - it's unreliable on broken systems.
// For example, on Ubuntu 8.04, /dev/disk/by-id contains two device
// links for two drives, but both point to the same sdb (instead of
// sda and sdb). Plus, there are no "*-partN" files (not that we need them).
/*
if (!found) {
error_msg = detect_drives_linux_udev_byid(devices); // linux udev
// we check for devices vector emptiness because it could be a dummy directory
// with no files, so treat it as an error.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
*/
if (!found) {
error_msg = detect_drives_linux_proc_partitions(devices); // linux /proc/partitions as fallback.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#elif defined CONFIG_KERNEL_FAMILY_WINDOWS
#elif defined CONFIG_KERNEL_WINDOWS
if (!found) {
error_msg = detect_drives_win32(devices); // win32
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
error_msg = detect_drives_win32(all_detected, ex_factory); // win32
#else // freebsd, etc...
error_msg = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev.
if (!found) {
error_msg = detect_drives_dev(devices); // bsd, etc... . scans /dev.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#endif
if (all_detected.empty()) {
if (!found) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n");
return error_msg; // last error message should be ok.
}
for (std::vector<StorageDeviceRefPtr>::iterator iter = all_detected.begin(); iter != all_detected.end(); ++iter) {
StorageDeviceRefPtr drive = *iter;
for (std::vector<std::string>::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) {
std::string dev = *iter;
// try to match against patterns
// for (unsigned int i = 0; i < match_patterns_.size(); i++) {
@@ -67,28 +609,26 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives, Ex
// matched, check the blacklist
bool blacked = false;
for (unsigned int j = 0; j < blacklist_patterns_.size(); j++) {
if (app_pcre_match(blacklist_patterns_[j], drive->get_device())) { // matched the blacklist too
if (app_pcre_match(blacklist_patterns_[j], dev)) { // matched the blacklist too
blacked = true;
break;
}
}
debug_out_info("app", "Found device: " << drive->get_device_with_type() << ".\n");
StorageDeviceRefPtr drive(new StorageDevice(dev));
debug_out_info("app", "Found device: \"" << drive->get_device() << "\".\n");
if (!blacked) {
drives.push_back(drive);
// break; // no need to match other patters, go to next device
} else { // blacklisted
debug_out_info("app", "Device " << drive->get_device_with_type() << " is blacklisted, ignoring.\n");
debug_out_info("app", "Device \"" << drive->get_device() << "\" is blacklisted, ignoring.\n");
// break; // go to next device
}
// }
}
// Sort the drives, because their order is not quite defined.
// TODO Sort using natural sort
std::sort(drives.begin(), drives.end());
}
debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n");
return std::string();
@@ -97,26 +637,27 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives, Ex
std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
ExecutorFactoryRefPtr ex_factory, bool return_first_error)
hz::intrusive_ptr<CmdexSync> smartctl_ex, bool return_first_error)
{
fetch_data_errors_.clear();
fetch_data_error_outputs_.clear();
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
// If it doesn't exist, create a default one. Even though it will be auto-created later,
// we need it here to get its errors afterwards.
if (!smartctl_ex)
smartctl_ex = new SmartctlExecutor(); // will be auto-deleted
for (unsigned int i = 0; i < drives.size(); ++i) {
StorageDeviceRefPtr drive = drives[i];
debug_out_info("app", "Retrieving basic information about the device...\n");
smartctl_ex->set_running_msg("Running %s on " + drive->get_device_with_type() + "...");
smartctl_ex->set_running_msg("Running %s on " + drive->get_device() + "...");
// don't show any errors here - we don't want a screen flood.
// no need for gui-based executors here, we already show the message in
// iconview background (if called from main window)
std::string error_msg;
if (drive->get_info_output().empty()) { // if not fetched during detection
error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
}
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
// normally we skip drives with errors - possibly scsi, etc...
if (return_first_error && !error_msg.empty())
@@ -131,10 +672,9 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>&
fetch_data_error_outputs_.push_back(smartctl_ex->get_stdout_str());
}
debug_out_dump("app", "Device information for " << drive->get_device()
<< " (type: \"" << drive->get_type_argument() << "\"):\n"
debug_out_dump("app", "Device information for " << drive->get_device() << ":\n"
<< "\tModel: " << drive->get_model_name() << "\n"
<< "\tDetected type: " << StorageDevice::get_type_readable_name(drive->get_detected_type()) << "\n"
<< "\tType: " << StorageDevice::get_type_name(drive->get_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n"
);
@@ -146,12 +686,12 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>&
std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
ExecutorFactoryRefPtr ex_factory)
hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
std::string error_msg = detect(put_drives_here, ex_factory);
std::string error_msg = detect(put_drives_here);
if (error_msg.empty())
fetch_basic_data(put_drives_here, ex_factory, false); // ignore its errors, there may be plenty of them.
fetch_basic_data(put_drives_here, smartctl_ex, false); // ignore its errors, there may be plenty of them.
return error_msg;
}
@@ -161,5 +701,3 @@ std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDevi
/// @}
+12 -29
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef STORAGE_DETECTOR_H
#define STORAGE_DETECTOR_H
@@ -19,37 +14,31 @@
#include "storage_device.h"
#include "cmdex_sync.h"
#include "executor_factory.h"
/// Storage detector - detects available drives in the system.
class StorageDetector {
public:
/// Constructor
StorageDetector()
{
// match_patterns_.push_back("/.+/"); // accept anything, the scanner picks up disks only anyway.
}
/// Detects a list of drives. Returns detection error message if error occurs.
std::string detect(std::vector<StorageDeviceRefPtr>& put_drives_here,
ExecutorFactoryRefPtr ex_factory);
// detects a list of drives. returns detection error if error occurs.
std::string detect(std::vector<StorageDeviceRefPtr>& put_drives_here);
/// For each drive, fetch basic data and parse it.
/// If \c return_first_error is true, the function returns on the first error.
/// \return An empty string. Or, if return_first_error is true, the first error that occurs.
// fetch basic data of "drives" elements
std::string fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
ExecutorFactoryRefPtr ex_factory, bool return_first_error = false);
hz::intrusive_ptr<CmdexSync> smartctl_ex = 0, bool return_first_error = false);
/// Run detect() and fetch_basic_data().
/// \return An error if such occurs.
// do both of the above, return detection error.
std::string detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
ExecutorFactoryRefPtr ex_factory);
hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
// void add_match_patterns(std::vector<std::string>& patterns)
@@ -58,21 +47,17 @@ class StorageDetector {
// }
/// Add device patterns to drive detection blacklist
void add_blacklist_patterns(std::vector<std::string>& patterns)
{
blacklist_patterns_.insert(blacklist_patterns_.end(), patterns.begin(), patterns.end());
}
/// Get all errors produced by fetch_basic_data().
const std::vector<std::string>& get_fetch_data_errors() const
{
return fetch_data_errors_;
}
/// Get command output for each error in get_fetch_data_errors().
const std::vector<std::string>& get_fetch_data_error_outputs() const
{
return fetch_data_error_outputs_;
@@ -81,11 +66,11 @@ class StorageDetector {
private:
// std::vector<std::string> match_patterns_; ///< First each file is matched against these
std::vector<std::string> blacklist_patterns_; ///< If a device matches these, it's ignored.
// std::vector<std::string> match_patterns_; // first each file is matched against these
std::vector<std::string> blacklist_patterns_; // and then these
std::vector<std::string> fetch_data_errors_; ///< Errors that have occurred
std::vector<std::string> fetch_data_error_outputs_; ///< Corresponding command outputs to fetch_data_errors_
std::vector<std::string> fetch_data_errors_; // errors not returned via functions
std::vector<std::string> fetch_data_error_outputs_; // corresponding outputs
};
@@ -95,5 +80,3 @@ class StorageDetector {
#endif
/// @}
-205
View File
@@ -1,205 +0,0 @@
/**************************************************************************
Copyright:
(C) 2011 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef STORAGE_DETECTOR_HELPERS_H
#define STORAGE_DETECTOR_HELPERS_H
#include <string>
#include <vector>
#include <glibmm.h>
#include "executor_factory.h"
#include "storage_device.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
/// Find and execute tw_cli with specified options, return its output through \c output.
/// \return error message
inline std::string execute_tw_cli(ExecutorFactoryRefPtr ex_factory, const std::string& command_options, std::string& output)
{
hz::intrusive_ptr<CmdexSync> executor = ex_factory->create_executor(ExecutorFactory::ExecutorTwCli);
std::string binary;
rconfig::get_data("system/tw_cli_binary", binary);
if (binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "tw_cli binary is not set in config.\n");
return "tw_cli binary is not specified in configuration.";
}
std::vector<std::string> binaries; // binaries to try
// Note: tw_cli is automatically added to PATH in windows, no need to look for it.
binaries.push_back(binary);
#ifdef CONFIG_KERNEL_LINUX
// tw_cli may be named tw_cli.x86 or tw_cli.x86_64 in linux
binaries.push_back(binary + ".x86_64"); // try this first
binaries.push_back(binary + ".x86");
#endif
for (std::size_t i = 0; i < binaries.size(); ++i) {
executor->set_command(Glib::shell_quote(binaries.at(i)), command_options);
if (!executor->execute() || !executor->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing tw_cli binary.\n");
} else {
break; // found it
}
}
// any_to_unix is needed for windows
output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "tw_cli returned an empty output.\n");
return "tw_cli returned an empty output.";
}
return std::string();
}
/// Get the drives on a 3ware controller using tw_cli.
/// Note that the drives are inserted in the order they are detected.
inline std::string tw_cli_get_drives(const std::string& dev, int controller,
std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev)
{
debug_out_info("app", "Getting available 3ware drives (ports) for controller " << controller << " through tw_cli...\n");
std::string output;
std::string error = execute_tw_cli(ex_factory, hz::string_sprintf("/c%d show all", controller), output);
if (!error.empty()) {
return error;
}
// split to lines
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
// Note that the ports may be printed in any order. We sort the drives themselves in the end.
pcrecpp::RE port_re = app_pcre_re("/^p([0-9]+)[ \\t]+([^\\t\\n]+)/mi");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string port_str, status;
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &port_str, &status)) {
if (status != "NOT-PRESENT") {
int port = -1;
hz::string_is_numeric(port_str, port);
if (port != -1) {
if (use_tw_cli_dev) { // use "tw_cli/cx/py" device
drives.push_back(StorageDeviceRefPtr(new StorageDevice("tw_cli/c"
+ hz::number_to_string(controller) + "/p" + hz::number_to_string(port))));
} else {
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "3ware," + hz::number_to_string(port))));
}
debug_out_info("app", "Added 3ware drive " << drives.back()->get_device_with_type() << ".\n");
}
}
}
}
return std::string();
}
/// Return 3ware SCSI host numbers (same as /c switch to tw_cli).
/// \return error string on error
inline std::string tw_cli_get_controllers(ExecutorFactoryRefPtr ex_factory, std::vector<int>& controllers)
{
debug_out_info("app", "Getting available 3ware controllers through tw_cli...\n");
std::string output;
std::string error = execute_tw_cli(ex_factory, "show", output);
if (!error.empty()) {
return error;
}
// split to lines
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
pcrecpp::RE controller_re = app_pcre_re("/^c([0-9]+)[ \\t]+/mi");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string controller_str;
if (controller_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &controller_str)) {
int controller = -1;
hz::string_is_numeric(controller_str, controller);
if (controller != -1) {
debug_out_info("app", "Found 3ware controller " << controller << ".\n");
controllers.push_back(controller);
}
}
}
// Sort them. This affects only the further detection order, since the drives
// are sorted in the end anyway.
std::sort(controllers.begin(), controllers.end());
return std::string();
}
/// Get number of ports by sequentially running smartctl on each port, until
/// one of the gives an error. \c type contains a printf-formatted string with %d.
/// \return an error message on error.
inline std::string smartctl_scan_drives_sequentially(const std::string& dev, const std::string& type,
int from, int to, std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, std::string& last_output)
{
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
for (int i = from; i <= to; ++i) {
std::string type_arg = hz::string_sprintf(type.c_str(), i);
StorageDeviceRefPtr drive(new StorageDevice(dev, type_arg));
// This will generate an error if smartctl doesn't return 0, which is what happens
// with non-populated ports.
// Sometimes the output contains:
// "Read Device Identity failed: Input/output error"
// or
// "Read Device Identity failed: empty IDENTIFY data"
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
last_output = drive->get_info_output();
// If we've reached smartctl port limit (older versions may have smaller limits), abort.
if (app_pcre_match("/VALID ARGUMENTS ARE/mi", last_output)) {
break;
}
// If we couldn't open the device, it means there is no such controller at specified device
// and scanning the ports is useless.
if (app_pcre_match("/No .* controller found/mi", last_output)
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output) ) {
break;
}
if (!error_msg.empty()) {
debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n");
debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n");
} else {
drives.push_back(drive);
debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n");
}
}
return std::string();
}
#endif
/// @}
File diff suppressed because it is too large Load Diff
-38
View File
@@ -1,38 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef STORAGE_DETECTOR_LINUX_H
#define STORAGE_DETECTOR_LINUX_H
#include "hz/hz_config.h" // CONFIG_*
#if defined CONFIG_KERNEL_LINUX
#include <string>
#include <vector>
#include "executor_factory.h"
#include "storage_device.h"
/// Detect drives in Linux
std::string detect_drives_linux(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
#endif
/// @}
-282
View File
@@ -1,282 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include "hz/hz_config.h" // CONFIG_*
#if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <algorithm> // std::sort
#if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
#include <util.h> // getrawpartition()
#endif
#include "hz/debug.h"
#include "hz/fs_path_utils.h"
#include "hz/fs_path.h"
#include "hz/fs_file.h"
#include "hz/fs_dir.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "storage_detector_other.h"
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through /dev...\n");
std::vector<std::string> devices;
std::string sdev_config_path;
#if defined CONFIG_KERNEL_SOLARIS
sdev_config_path = "system/solaris_dev_path";
#else // other unixes
sdev_config_path = "system/unix_sdev_path";
#endif
std::string dev_dir; // defaults to /dev for freebsd, /dev/rdsk for solaris.
if (!rconfig::get_data(sdev_config_path, dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n");
return "Device directory path is not set.";
}
hz::Dir dir(dev_dir);
std::vector<std::string> all_devices;
if (!dir.list(all_devices, false, hz::DirSortNone())) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
}
return error_msg;
}
// platform whitelist
std::vector<std::string> whitelist;
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY
// FreeBSD example device names:
// * /dev/ad0 - ide disk 0. /dev/ad0a - non-slice dos partition 1.
// * /dev/da1s1e - scsi disk 1 (second disk), slice 1 (aka dos partition 1),
// bsd partition e (multiple bsd partitions may be inside one dos partition).
// * /dev/acd0 - first cdrom. /dev/acd0c - ?.
// Info: http://www.freebsd.org/doc/en/books/handbook/disks-naming.html
// Of two machines I had access to, freebsd 6.3 had only real devices,
// while freebsd 4.10 had lots of dummy ones (ad1, ad2, ad3, da0, da1, da2, da3, sa0, ...).
whitelist.push_back("/^ad[0-9]+$/"); // adN without suffix - fbsd ide
whitelist.push_back("/^da[0-9]+$/"); // daN without suffix - fbsd scsi, usb
whitelist.push_back("/^ada[0-9]+$/"); // adaN without suffix - fbsd ata cam
// whitelist.push_back("/ ^sa[0-9]+$/"); // saN without suffix - fbsd scsi tape
// whitelist.push_back("/^ast[0-9]+$/"); // astN without suffix - fbsd ide tape
whitelist.push_back("/^aacd[0-9]+$/"); // fbsd adaptec raid
whitelist.push_back("/^mlxd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^mlyd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^amrd[0-9]+$/"); // fbsd AMI raid
whitelist.push_back("/^idad[0-9]+$/"); // fbsd compaq raid
whitelist.push_back("/^twed[0-9]+$/"); // fbsd 3ware raid
// these are checked by smartctl, but they are not mentioned in freebsd docs:
whitelist.push_back("/^tw[ae][0-9]+$/"); // fbsd 3ware raid
// unused: acd - ide cdrom, cd - scsi cdrom, mcd - mitsumi cdrom, scd - sony cdrom,
// fd - floppy, fla - diskonchip flash.
#elif defined CONFIG_KERNEL_SOLARIS
// /dev/rdsk contains "raw" physical char devices, as opposed to
// "filesystem" block devices in /dev/dsk. smartctl needs rdsk.
// x86:
// /dev/rdsk/c0t0d0p0:1
// Where c0 is the controller number.
// t0 is the target (SCSI ID number) (omit for ATAPI)
// d0 is always 0 for SCSI, the drive # for ATAPI
// p0 is the partition (p0 is the entire disk, or p1 - p4)
// :1 is the logical drive (c - z or 1 - 24) (that is, logical partitions inside extended partition)
// /dev/rdsk/c0d0p2:1
// where p2 means the extended partition (type 0x05) is partition 2 (out of 1-4) and
// ":1" is the 2nd extended partition (:0 would be the first extended partition). -- not sure about this!
// p0 whole physical disk
// p1 - p4 Four primary fdisk partitions
// p5 - p30 26 logical drives in extended DOS partition (not implemented by Solaris)
// s0 - s15 16 slices in the Solaris FDISK partition (SPARC has only 8)
// On Solaris, by convention, s2 is the whole Solaris partition.
// SPARC: There are no *p* devices on sparc afaik, only slices. By convention,
// s2 is used as a "whole" disk there (but not with EFI partitions!).
// So, c0d0s2 is the whole disk there.
// x86 has both c0d0s2 and c0d0p2, where s2 is a whole solaris partition.
// NOTE: It seems that smartctl searches for s0 (aka root partition) in the end. I'm not sure
// what implications this has for x86, but we replicate this behaviour.
// Note: Cdroms are ATAPI, so they pose as SCSI, as opposed to IDE hds.
// TODO: No idea how to implement /dev/rmt (scsi tape devices),
// I have no files in that directory.
whitelist.push_back("/^c[0-9]+(?:t[0-9]+)?d[0-9]+s0$/");
#elif defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// OpenBSD / NetBSD have /dev/wdNc for IDE/ATA, /dev/sdNc for SCSI disk,
// /dev/stNc for scsi tape. N is [0-9]+. "c" means "whole disk" (not sure about
// different architectures though). There are no "sdN" devices, only "sdNP".
// Another manual says that wd0d would be a whole disk, while wd0c is its
// bsd part only (on x86) (only on netbsd?). Anyway, getrawpartition() gives
// us the letter we need.
// There is no additional level of names for BSD subpartitions (unlike freebsd).
// cd0a is cdrom.
// Dummy devices are present.
// Note: This detection may take a while (probably due to open() check).
char whole_part = 'a' + getrawpartition(); // from bsd's util.h
whitelist.push_back(hz::string_sprintf("/^wd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^sd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^st[0-9]+%c$/", whole_part));
#elif defined CONFIG_KERNEL_DARWIN
// Darwin has /dev/disk0, /dev/disk0s1, etc...
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^disk[0-9]+$/");
#elif defined CONFIG_KERNEL_QNX
// QNX has /dev/hd0, /dev/hd0t78 (partition?).
// Afaik, IDE and SCSI have the same prefix. fd for floppy, cd for cdrom.
// Not sure about the tapes.
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^hd[0-9]+$/");
#endif // unix platforms
std::vector<std::string> matched_devices;
for (std::size_t i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
bool matched = false;
for (std::vector<std::string>::const_iterator iter = whitelist.begin(); iter != whitelist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
matched = true;
break;
}
}
if (!matched)
continue;
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// In case these are links, trace them to originals.
// We don't replace /dev files with real devices - it leads to really bad paths (pci ids for solaris, etc...).
std::string link_dest;
hz::FsPath real_dev_path;
if (path.get_link_destination(link_dest)) {
real_dev_path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
real_dev_path.compress(); // compress in case there are ../-s.
// solaris has dangling links for non-existant devices, so filter them out.
if (!real_dev_path.exists())
continue;
}
matched_devices.push_back(path.str());
}
// List ones who need dummy device filtering
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY \
|| defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// Since we may have encountered dummy devices, we should check
// if they really exist. Unfortunately, this involves opening them, which
// is not good with some media (e.g. it may hang on audio cd (freebsd, maybe others), etc...).
// That's why we don't whitelist cd devices. Let's hope usb and others are
// ok with this. Dummy devices give errno ENXIO (6, Device not configured) on *bsd.
// In Linux ENXIO is (6, No such device or address).
// Don't do this on solaris - we can't distinguish between cdroms and hds there.
// If there are less than 4 devices, they are probably not dummy (newer freebsd).
bool open_needed = (matched_devices.size() >= 4);
if (open_needed) {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", will try to filter non-existent ones out.\n");
} else {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", no need for filtering them out.\n");
}
for (std::size_t i = 0; i < matched_devices.size(); ++i) {
hz::File dev_file(matched_devices[i]);
// we check errno because OS may deny us for some other reason on valid devices.
if (open_needed && !dev_file.open("rb") && dev_file.get_errno() == ENXIO) {
debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" failed to open, ignoring.\n");
continue;
}
if (open_needed)
debug_out_info("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" opened successfully, adding to device list.\n");
devices.push_back(dev_file.str());
}
#else // not *BSD
for (std::size_t i = 0; i < matched_devices.size(); ++i) {
debug_out_info("app", DBG_FUNC_MSG << "Device \"" << matched_devices[i] << "\" matched the whitelist, adding to device list.\n");
devices.push_back(matched_devices[i]);
}
#endif
// TODO Sort using natural sort
std::sort(devices.begin(), devices.end());
for (std::size_t i = 0; i < devices.size(); ++i) {
drives.push_back(StorageDeviceRefPtr(new StorageDevice(devices.at(i))));
}
return std::string();
}
#endif // !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
/// @}
-37
View File
@@ -1,37 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef STORAGE_DETECTOR_OTHER_H
#define STORAGE_DETECTOR_OTHER_H
#include "hz/hz_config.h" // CONFIG_*
#if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <string>
#include <vector>
#include "executor_factory.h"
#include "storage_device.h"
/// Detect drives in FreeBSD, Solaris, etc... (all except Linux and Windows).
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
#endif
/// @}
+4 -15
View File
@@ -1,13 +1,8 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib_tests
/// \weakgroup applib_tests
/// @{
#include <iostream>
@@ -16,8 +11,6 @@
#include "gsc_settings.h" // in src directory
/// Main function of the test
int main()
{
// These settings contain device search paths, smartctl binary, etc...
@@ -31,15 +24,13 @@ int main()
// sd.add_match_patterns(match_patterns);
sd.add_blacklist_patterns(blacklist_patterns);
ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(false));
std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory);
std::string error_msg = sd.detect_and_fetch_basic_data(drives);
if (!error_msg.empty()) {
std::cerr << error_msg << "\n";
} else {
for (unsigned int i = 0; i < drives.size(); ++i) {
std::cerr << drives[i]->get_device_with_type() <<
" (" << StorageDevice::get_type_readable_name(drives[i]->get_detected_type()) << ")\n";
std::cerr << drives[i]->get_device() << " (" << StorageDevice::get_type_name(drives[i]->get_type()) << ")\n";
}
}
@@ -48,5 +39,3 @@ int main()
/// @}
-812
View File
@@ -1,812 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include "hz/hz_config.h" // CONFIG_*
#if defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
#include <glibmm.h>
#include <set>
#include <bitset>
#include <map>
#include <vector>
#include "hz/win32_tools.h"
#include "hz/string_sprintf.h"
#include "hz/fs_path.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "storage_detector_win32.h"
#include "storage_detector_helpers.h"
/**
\file
<pre>
3ware Windows detection:
For 3ware 9xxx only.
Call as: smartctl -i sd[a-z],N
N is port, a-z is logical drive (unit) provided by controller.
N is limited to [0, 31] in the code.
The sd[a-z] device actually exists as \\\\.\\PhysicalDrive[0-N].
No idea how to check if it's 3ware.
Call as: smarctl -i tw_cli/cx/py
This runs tw_cli tool and parses the output; controller x, port y.
tw_cli may be needed for older controllers / drivers.
In tw_cli mode only limited information-gathering is supported.
tw_cli (part of 3DM2) is automatically added to system PATH,
no need to look for it.
3DM2 install can be detected by checking:
HKEY_USERS\\.DEFAULT\\Software\\3ware\\3DM2, InstallPath
Another option for detection (whether it's 3ware) would be getting
\\\\.\\PhysicalDrive0 properties, like smartctl does.
Newer (added after 5.39.1) smartctl supports --scan-open, which will give us:
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
-d 3ware is not needed under Windows. We should treat sda as pd0
and remove pd0 from PhysicalDrive-detected list.
Running smartctl on sda gives almost the same result as on sda,0.
smartctl --scan-open output for win32 with 3ware RAID:
------------------------------------------------------------------
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
------------------------------------------------------------------
Intel Matrix RAID (since smartmontools SVN version on 2011-02-04):
Call as: "/dev/csmi[0-9],N" where N is the port behind the logical
scsi controller "\\\\.\\Scsi[0-9]:".
The prefix "/dev/" is optional.
This is detected (with /dev/ prefix) by --scan-open.
The drives may be duplicated as pdX (with X and N being unrelated).
This usually happens when Intel RAID drivers are installed, even
if no RAID configuration is present. For example, on a laptop with
Intel chipset and Intel drivers installed, we have /dev/csmi0,0 and
pd0 for the first HDD, and /dev/csmi0,1 for DVD (no pdX entry there).
This is what --scan-open looks like:
------------------------------------------------------------------
/dev/sda -d ata # /dev/sda, ATA device
/dev/csmi0,0 -d ata # /dev/csmi0,0, ATA device
/dev/csmi0,1 -d ata # /dev/csmi0,1, ATA device
------------------------------------------------------------------
We filter out pdX devices using serial numbers (unless "-q noserial"
is given to smartctl), and prefer csmi to pd (since csmi provides more features).
</pre>
*/
struct DriveLetterInfo {
std::set<int> physical_drives; ///< N in pdN
std::string volume_name; ///< Volume name
};
namespace {
/// Check which physical drives each drive letter (C, D, ...) spans across.
std::map<char, DriveLetterInfo> win32_get_drive_letter_map()
{
std::map<char, DriveLetterInfo> drive_letter_map;
std::bitset<32> drives(GetLogicalDrives());
// Check which drives are fixed
std::vector<char> good_drives;
for (char c = 'A'; c <= 'Z'; ++c) {
if (drives[c - 'A']) { // drive is present
debug_out_dump("app", "Windows drive found: " << c << ".\n");
if (GetDriveType((c + std::string(":\\")).c_str()) == DRIVE_FIXED) {
debug_out_dump("app", "Windows drive " << c << " is fixed.\n");
good_drives.push_back(c);
}
}
}
// Try to open each drive, check its disk extents
for (std::size_t i = 0; i < good_drives.size(); ++i) {
char drive = good_drives[i];
std::string drive_str = std::string("\\\\.\\") + drive + ":";
HANDLE h = CreateFileA(
drive_str.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS, NULL);
if (h == INVALID_HANDLE_VALUE) {
debug_out_warn("app", "Windows drive " << drive << " cannot be opened.\n");
continue;
}
DWORD bytesReturned = 0;
VOLUME_DISK_EXTENTS vde;
if (!DeviceIoControl(
h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL)) {
debug_out_warn("app", "Windows drive " << drive << " is not mapped to any physical drives.\n");
continue;
}
std::set<int> physical_drives;
for (int i = 0; i < vde.NumberOfDiskExtents; ++i) {
physical_drives.insert(vde.Extents[i].DiskNumber);
debug_out_dump("app", "Windows drive " << drive << " corresponds to physical drive " << vde.Extents[i].DiskNumber << ".\n");
}
std::string volume_name;
wchar_t volume_name_w[MAX_PATH+1] = {0};
DWORD dummy = 0;
hz::scoped_array<wchar_t> drive_name(hz::win32_utf8_to_utf16((drive + std::string(":\\")).c_str()));
if (drive_name && GetVolumeInformationW(drive_name.get(),
volume_name_w, MAX_PATH+1,
NULL, &dummy, &dummy, NULL, 0)) {
volume_name = hz::win32_utf16_to_utf8_string(volume_name_w);
}
DriveLetterInfo dli;
dli.physical_drives = physical_drives;
dli.volume_name = volume_name;
drive_letter_map[drive] = dli;
}
return drive_letter_map;
}
/// Run "smartctl --scan-open" and pick the devices which have
/// a port parameter. We don't pick the others because the may
/// conflict with pd* devices, and we like pd* better than sd*.
std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& drives,
ExecutorFactoryRefPtr ex_factory,
const std::map<char, DriveLetterInfo>& drive_letter_map,
std::set<int>& equivalent_pds)
{
debug_out_info("app", "Getting multi-port devices through smartctl --scan-open...\n");
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
std::string smartctl_binary = get_smartctl_binary();
if (smartctl_binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n");
return "Smartctl binary is not specified in configuration.";
}
std::string smartctl_def_options;
rconfig::get_data("system/smartctl_options", smartctl_def_options);
if (!smartctl_def_options.empty())
smartctl_def_options += " ";
smartctl_ex->set_command(Glib::shell_quote(smartctl_binary),
smartctl_def_options + "--scan-open");
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n");
return smartctl_ex->get_error_msg();
}
// any_to_unix is needed for windows
std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
}
// if we've reached smartctl port limit (older versions may have smaller limits), abort.
if (app_pcre_match("/UNRECOGNIZED OPTION/mi", output)) {
return "Smartctl doesn't support --scan-open switch.";
}
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
// /dev/sda,0 -d ata (opened)
// /dev/sda,1 -d ata (opened)
// /dev/sda -d sat # /dev/sda [SAT], ATA device
// /dev/sde,2 -d ata [ATA] (opened)
// we only pick the ones with ports
const pcrecpp::RE port_re = app_pcre_re("/^(/dev/[a-z0-9]+),([0-9]+)[ \\t]+-d[ \\t]+([^ \\t\\n]+)/i");
const pcrecpp::RE dev_re = app_pcre_re("/^/dev/sd([a-z])$/");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string dev, port_str, type;
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &dev, &port_str, &type)) {
std::string sd_letter;
int drive_num = -1;
if (dev_re.PartialMatch(dev, &sd_letter)) {
// don't use pd* devices equivalent to these sd* devices.
drive_num = sd_letter.at(0) - 'a';
equivalent_pds.insert(drive_num);
}
std::string full_dev = dev + "," + port_str;
StorageDeviceRefPtr drive(new StorageDevice(full_dev, type));
std::map<char, std::string> letters_volnames;
for (std::map<char, DriveLetterInfo>::const_iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) {
if (iter->second.physical_drives.count(drive_num) > 0) {
letters_volnames[iter->first] = iter->second.volume_name;
}
}
drive->set_drive_letters(letters_volnames);
drives.push_back(drive);
}
}
return std::string();
}
/// Find and execute areca cli with specified options, return its output through \c output.
/// \return error message
inline std::string execute_areca_cli(ExecutorFactoryRefPtr ex_factory, const std::string& cli_binary,
const std::string& command_options, std::string& output)
{
hz::intrusive_ptr<CmdexSync> executor = ex_factory->create_executor(ExecutorFactory::ExecutorArecaCli);
executor->set_command(Glib::shell_quote(cli_binary), command_options);
if (!executor->execute() || !executor->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing Areca cli binary.\n");
}
// any_to_unix is needed for windows
output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Areca cli returned an empty output.\n");
return "Areca CLI returned an empty output.";
}
return std::string();
}
/** <pre>
Get the drives on Areca controller using Areca cli tool.
Note that the drives are inserted in the order they are detected.
There are 3 formats of "disk info" output:
1. No expanders:
------------------------------------------------------------
# Ch# ModelName Capacity Usage
===============================================================================
1 1 INTEL SSDSA2M160G2GC 160.0GB System
2 2 INTEL SSDSA2M160G2GC 160.0GB System
3 3 INTEL SSDSA2M160G2GC 160.0GB System
4 4 INTEL SSDSA2M160G2GC 160.0GB System
5 5 Hitachi HDS724040ALE640 4000.8GB Storage
6 6 Hitachi HDS724040ALE640 4000.8GB Storage
7 7 Hitachi HDS724040ALE640 4000.8GB Storage
8 8 Hitachi HDS724040ALE640 4000.8GB Storage
9 9 Hitachi HDS724040ALE640 4000.8GB Storage
10 10 Hitachi HDS724040ALE640 4000.8GB Storage
11 11 Hitachi HDS724040ALE640 4000.8GB Backup
12 12 Hitachi HDS724040ALE640 4000.8GB Backup
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------
2. No expanders (this output comes from CLI documentation, possibly an old format):
------------------------------------------------------------
# ModelName Serial# FirmRev Capacity State
===============================================================================
1 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
2 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
....(snip)....
12 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------
3. Expanders:
------------------------------------------------------------
# Enc# Slot# ModelName Capacity Usage
===============================================================================
1 01 Slot#1 N.A. 0.0GB N.A.
2 01 Slot#2 N.A. 0.0GB N.A.
3 01 Slot#3 N.A. 0.0GB N.A.
4 01 Slot#4 N.A. 0.0GB N.A.
5 01 Slot#5 N.A. 0.0GB N.A.
6 01 Slot#6 N.A. 0.0GB N.A.
7 01 Slot#7 N.A. 0.0GB N.A.
8 01 Slot#8 N.A. 0.0GB N.A.
9 02 SLOT 01 N.A. 0.0GB N.A.
10 02 SLOT 02 N.A. 0.0GB N.A.
11 02 SLOT 03 N.A. 0.0GB N.A.
12 02 SLOT 04 N.A. 0.0GB N.A.
13 02 SLOT 05 N.A. 0.0GB N.A.
14 02 SLOT 06 N.A. 0.0GB N.A.
15 02 SLOT 07 N.A. 0.0GB N.A.
16 02 SLOT 08 N.A. 0.0GB N.A.
17 02 SLOT 09 N.A. 0.0GB N.A.
18 02 SLOT 10 N.A. 0.0GB N.A.
19 02 SLOT 11 N.A. 0.0GB N.A.
20 02 SLOT 12 N.A. 0.0GB N.A.
21 02 SLOT 13 N.A. 0.0GB N.A.
22 02 SLOT 14 ST910021AS 100.0GB Free
23 02 SLOT 15 WDC WD3200BEVT-75A23T0 320.1GB HotSpare[Global]
24 02 SLOT 16 N.A. 0.0GB N.A.
25 02 SLOT 17 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
26 02 SLOT 18 ST31500341AS 1500.3GB Raid Set # 000
27 02 SLOT 19 ST3320620AS 320.1GB Raid Set # 000
28 02 SLOT 20 ST31500341AS 1500.3GB Raid Set # 000
29 02 SLOT 21 ST3500320AS 500.1GB Raid Set # 000
30 02 SLOT 22 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
31 02 SLOT 23 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
32 02 SLOT 24 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
33 02 EXTP 01 N.A. 0.0GB N.A.
34 02 EXTP 02 N.A. 0.0GB N.A.
35 02 EXTP 03 N.A. 0.0GB N.A.
36 02 EXTP 04 N.A. 0.0GB N.A.
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------
</pre> */
inline std::string areca_cli_get_drives(const std::string& cli_binary, const std::string& dev, int controller,
std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", "Getting available Areca drives (ports) for controller " << controller << " through Areca CLI...\n");
// TODO Support controller number.
// So far it seems the only way to pass the controller number to CLI is to use
// the interactive mode.
std::string output;
std::string error = execute_areca_cli(ex_factory, cli_binary, "disk info", output);
if (!error.empty()) {
return error;
}
// split to lines
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
enum FormatType {
FormatUnknown,
FormatNoEnc1,
FormatNoEnc2,
FormatEnc
};
pcrecpp::RE noenc1_header_re = app_pcre_re("/^\\s*#\\s+Ch#/mi");
pcrecpp::RE noenc2_header_re = app_pcre_re("/^\\s*#\\s+ModelName/mi");
pcrecpp::RE exp_header_re = app_pcre_re("/^\\s*#\\s+Enc#/mi");
FormatType format_type = FormatUnknown;
for (std::size_t i = 0; i < lines.size(); ++i) {
if (noenc1_header_re.PartialMatch(lines.at(i))) {
format_type = FormatNoEnc1;
break;
} else if (noenc2_header_re.PartialMatch(lines.at(i))) {
format_type = FormatNoEnc2;
break;
} else if (exp_header_re.PartialMatch(lines.at(i))) {
format_type = FormatEnc;
break;
}
}
if (format_type == FormatUnknown) {
debug_out_warn("app", "Could not read Areca CLI output: No valid header found.\n");
return "Could not read Areca CLI output: No valid header found.";
}
// Note: These may not match the full model, but just the first part is sufficient for comparison with "N.A.".
pcrecpp::RE noexp1_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model.
pcrecpp::RE noexp2_port_re = app_pcre_re("/^\\s*([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model.
pcrecpp::RE exp_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+(?:Slot#|SLOT\\s+)([0-9]+)\\s+([^\\s]+)/mi"); // matches enclosure, port, model.
bool has_enclosure = (format_type == FormatEnc);
if (has_enclosure) {
debug_out_dump("app", "Areca controller seems to have enclosures.\n");
} else {
debug_out_dump("app", "Areca controller doesn't have any enclosures.\n");
}
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string port_str, model_str;
if (has_enclosure) {
std::string enclosure_str;
if (exp_port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &enclosure_str, &port_str, &model_str)) {
if (model_str != "N.A.") {
int port = -1, enclosure = -1;
hz::string_is_numeric(port_str, port);
hz::string_is_numeric(enclosure_str, enclosure);
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev,
"areca," + hz::number_to_string(port) + "/" + hz::number_to_string(enclosure))));
debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n");
}
}
} else { // no enclosures
pcrecpp::RE port_re = (format_type == FormatNoEnc1 ? noexp1_port_re : noexp2_port_re);
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &port_str, &model_str)) {
if (model_str != "N.A.") {
int port = -1;
hz::string_is_numeric(port_str, port);
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "areca," + hz::number_to_string(port))));
debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n");
}
}
}
}
return std::string();
}
/** <pre>
Areca Windows:
Call as:
- smartctl -i -d areca,N /dev/arcmsr0 (N is [1,24]).
- smartctl -i -d areca,N/E /dev/arcmsr0 (N is [1,128], E is [1,8]).
The models that have "ix" (case-insensitive) in their model names
have the enclosures and require the N,E syntax.
Note: Using N/E syntax with non-enclosure cards seems to work
regardless of the value of E.
Detection:
Check the registry keys for Areca management software and
the CLI utility. One of them has to be installed.
Check if CLI is installed. If it is, run it and parse the output. This should
give us the populated port list (non-enclosure), and the populated port
and expander list (enclosure models).
There are two problems with CLI:
If no controller is present, it crashes.
We try the following first:
smartctl -i -d areca,1 /dev/arcmsr0
If it contains "arcmsr0: No Areca controller found" (Windows), or
"Smartctl open device: /dev/arcmsr0 [areca_disk#01_enc#01] failed: No such device" (Linux),
then there's no controller there.
I don't see how it reports different controllers (there's only one list).
We ignore this for now.
If CLI is not installed, do the brute-force way:
For arcmsr0, 1, ..., 8 (until we see "No Areca controller found"):
Scan -d areca,[1-24] /dev/arcmsrN
If at least one drive was found, it's a no-enclosure controller.
If no drives were found, it's probably an enclosure controller, try
-d areca,[1-128]/[1-8] /dev/arcmsrN
It's 2-3 drives a second on an empty port, so some limits are set in config.
</pre> */
inline std::string detect_drives_win32_areca(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n");
int scan_controllers = rconfig::get_data<int32_t>("system/win32_areca_scan_controllers");
if (scan_controllers == 0) { // disabled
debug_out_info("app", "Areca controller scanning is disabled through config.\n");
return std::string();
}
// Check if Areca tools are installed
std::string cli_inst_path;
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path);
if (cli_inst_path.empty()) {
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path);
}
if (scan_controllers == 2) { // auto
std::string http_inst_path;
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path);
if (http_inst_path.empty()) {
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path);
}
if (cli_inst_path.empty() && http_inst_path.empty()) {
debug_out_info("app", "No Areca software found. Install Areca CLI or set \"system/win32_areca_scan_controllers\" config key to 1 to force scanning.\n");
return std::string();
}
if (http_inst_path.empty()) {
debug_out_dump("app", "Areca HTTP installation not found.\n");
} else {
debug_out_dump("app", "Areca HTTP installation found at: \"" << http_inst_path << "\".\n");
}
}
if (cli_inst_path.empty()) {
debug_out_dump("app", "Areca CLI installation not found.\n");
} else {
debug_out_dump("app", "Areca CLI installation found at: \"" << cli_inst_path << "\".\n");
}
bool cli_found = !cli_inst_path.empty();
int use_cli = rconfig::get_data<int32_t>("system/win32_areca_use_cli");
bool scan_detect = (use_cli != 1); // Whether to detect using sequential port scanning. Only do that if CLI is not forced.
if (use_cli == 2) { // auto
use_cli = cli_found;
}
std::string cli_binary;
if (use_cli == 1) {
cli_binary = rconfig::get_data<std::string>("system/areca_cli_binary");
if (!hz::FsPath(cli_binary).is_absolute() && !cli_inst_path.empty()) {
cli_binary = hz::FsPath(cli_inst_path).append(cli_binary).str();
}
if (hz::FsPath(cli_binary).is_absolute() && !hz::FsPath(cli_binary).exists()) {
use_cli = 0;
debug_out_dump("app", "Areca CLI binary \"" << cli_binary << "\" not found.\n");
}
}
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
// --- CLI mode
// Since CLI may segfault if there are no drives, test the controller presence first.
// It doesn't matter if we use areca,N or areca,N/E - we will still get a different
// error if there's no controller.
if (use_cli) {
debug_out_dump("app", "Testing Areca controller presence using smartctl...\n");
StorageDeviceRefPtr drive(new StorageDevice("/dev/arcmsr0", "areca,1"));
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
std::string output = drive->get_info_output();
if (app_pcre_match("/No Areca controller found/mi", output)
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", output) ) {
use_cli = 0;
scan_detect = false;
debug_out_dump("app", "Areca controller not found.\n");
}
}
if (use_cli) {
debug_out_info("app", "Scanning Areca drives using CLI...\n");
int cli_max_controllers = 1; // TODO controller # with CLI.
for (int controller_no = 0; controller_no < cli_max_controllers; ++controller_no) {
std::string error_msg = areca_cli_get_drives(cli_binary, "/dev/arcmsr" + hz::number_to_string(controller_no), controller_no, drives, ex_factory);
// If we get an error on controller 0, fall back to no-cli detection.
if (!error_msg.empty() && controller_no == 0) {
use_cli = 0;
debug_out_warn("app", "Areca scan using CLI failed.\n");
if (scan_detect) {
debug_out_dump("app", "Trying manual Areca scan because of a CLI error.\n");
}
}
}
}
// If CLI scanning failed (and it was not forced), or if there was no CLI, scan manually.
if (use_cli == 0 && scan_detect) {
debug_out_info("app", "Manually scanning Areca controllers and ports...\n");
int max_controllers = rconfig::get_data<int32_t>("system/win32_areca_max_controllers");
int max_noenc_ports = rconfig::get_data<int32_t>("system/win32_areca_neonc_max_scan_port");
max_noenc_ports = std::max(1, std::min(24, max_noenc_ports)); // 1-24 sanity check
int max_enc_ports = rconfig::get_data<int32_t>("system/win32_areca_enc_max_scan_port");
max_enc_ports = std::max(1, std::min(128, max_enc_ports)); // 1-128 sanity check
int max_enclosures = rconfig::get_data<int32_t>("system/win32_areca_enc_max_enclosure");
max_enclosures = std::max(1, std::min(8, max_enclosures)); // 1-8 sanity check
for (int controller_no = 0; controller_no < max_controllers; ++controller_no) {
std::string dev = std::string("/dev/arcmsr") + hz::number_to_string(controller_no);
// First, scan using the areca,N format.
debug_out_dump("app", "Starting brute-force port scan (no-enclosure) on 1-" << max_noenc_ports << " ports, device \"" << dev
<< "\". Change the maximum by setting \"system/win32_areca_neonc_max_scan_port\" config key.\n");
std::size_t old_drive_count = drives.size();
std::string last_output;
std::string error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_noenc_ports, drives, ex_factory, last_output);
// If the scan stopped because of no controller, stop it all.
if (!error_msg.empty() && (app_pcre_match("/No Areca controller found/mi", last_output)
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output)) ) {
debug_out_dump("app", "Areca controller " << controller_no << " not present, stopping sequential scan.\n");
break;
}
// If no drives were added (but the controller is present), it may be due to
// expander syntax requirement. Try that.
if (drives.size() == old_drive_count) {
for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) {
debug_out_dump("app", "Starting brute-force port scan (enclosure #" << enclosure_no << ") on 1-" << max_enc_ports << " ports, device \"" << dev
<< "\". Change the maximums by setting \"system/win32_areca_onc_max_scan_port\" and \"system/win32_areca_enc_max_enclosure\" config keys.\n");
error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string(enclosure_no), 1, max_enc_ports, drives, ex_factory, last_output);
}
}
debug_out_dump("app", "Brute-force port scan finished.\n");
}
}
debug_out_info("app", DBG_FUNC_MSG << "Finished scanning Areca controllers.\n");
return std::string();
}
}
// smartctl accepts various variants, the most straight being pdN,
// (or /dev/pdN, /dev/ being optional) where N comes from
// "\\.\PhysicalDriveN" (winnt only).
// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> error_msgs;
std::string error_msg;
// Construct drive letter map
debug_out_info("app", "Checking which drive corresponds to which \\\\.\\PhysicalDriveN device...\n");
std::map<char, DriveLetterInfo> drive_letter_map = win32_get_drive_letter_map();
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
// Fetch multiport devices using --scan-open.
// Note that this may return duplicates (e.g. /dev/sda and /dev/csmi0,0)
std::set<int> used_pds;
error_msg = get_scan_open_multiport_devices(drives, ex_factory, drive_letter_map, used_pds);
if (!error_msg.empty()) {
error_msgs.push_back(error_msg);
}
bool multiport_found = !drives.empty();
bool areca_open_found = false; // whether areca devices were found at --scan-open time.
// Find out their serial numbers and whether there are Arecas there.
std::map<std::string, StorageDeviceRefPtr> serials;
for (std::size_t i = 0; i < drives.size(); ++i) {
std::string local_error = drives.at(i)->fetch_basic_data_and_parse(smartctl_ex);
if (!local_error.empty()) {
debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n");
// Don't exit, just report it.
}
if (!drives.at(i)->get_serial_number().empty()) {
// add model as well, who knows, there may be duplicates across vendors
std::string drive_serial_id = drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number();
serials[drive_serial_id] = drives.at(i);
}
// See if there are any areca devices. This is not implemented yet by smartctl (as of 6.0),
// but if it ever is, we can skip our own detection below.
std::string type_arg = drives.at(i)->get_type_argument();
if (type_arg.find("areca") != std::string::npos) {
areca_open_found = true;
}
}
// Scan PhysicalDriveN entries
debug_out_info("app", "Starting sequential scan of \\\\.\\PhysicalDriveN devices...\n");
int num_failed = 0;
for (int drive_num = 0; ; ++drive_num) {
// If the drive was already encountered in --scan-open (with a port number), skip it.
if (used_pds.count(drive_num) > 0) {
debug_out_dump("app", "pd" << drive_num << " already encountered in --scan-open output (as sd*), skipping.\n");
continue;
}
std::string phys_name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num);
// If the drive is openable, then it's there. Yes, CreateFile() is open, not create.
// NOTE: Administrative privileges are required to open it.
// We don't use any long/unopenable files here, so use the ANSI version.
HANDLE h = CreateFileA(phys_name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
// The numbers are usually consecutive, but sometimes there are holes when
// removable devices are removed. Try 3 extra drives just in case.
if (h == INVALID_HANDLE_VALUE) {
++num_failed;
debug_out_dump("app", "Could not open \"" << phys_name << "\".\n");
if (num_failed >= 3) {
debug_out_dump("app", "Stopping sequential scan.\n");
break;
} else {
continue;
}
}
CloseHandle(h);
debug_out_dump("app", "Successfully opened \"" << phys_name << "\".\n");
StorageDeviceRefPtr drive(new StorageDevice(hz::string_sprintf("pd%d", drive_num)));
std::map<char, std::string> letters_volnames;
for (std::map<char, DriveLetterInfo>::const_iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) {
if (iter->second.physical_drives.count(drive_num) > 0) {
letters_volnames[iter->first] = iter->second.volume_name;
}
}
drive->set_drive_letters(letters_volnames);
debug_out_dump("app", "Drive letters for: " << drive->get_device() << ": " << drive->format_drive_letters(true) << ".\n");
// Sometimes, a single physical drive may be accessible from both "/.//PhysicalDriveN"
// and "/.//Scsi2" (e.g. pd0 and csmi2,1). Prefer the port-having ones (which is from --scan-open),
// they contain more information.
// The only way to detect these duplicates is to compare them using serial numbers.
if (!serials.empty()) {
std::string local_error = drive->fetch_basic_data_and_parse(smartctl_ex);
if (!local_error.empty()) {
debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n");
// Don't exit, just report it.
}
std::string drive_serial_id = drive->get_model_name() + "_" + drive->get_serial_number();
// A serial may be empty if "-q noserial" was given to smartctl.
if (!drive->get_serial_number().empty() && serials.count(drive_serial_id) > 0) {
debug_out_info("app", "Skipping drive due to duplicate S/N: model: \"" << drive->get_model_name()
<< "\", S/N: \"" << drive->get_serial_number() << "\".\n");
// Copy the drive letters over to previously detected one (since we can't detect drive letters there).
serials[drive_serial_id]->set_drive_letters(drive->get_drive_letters());
continue;
}
}
debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n");
drives.push_back(drive);
}
// If smartctl --scan-open returns no "sd*,port"-style devices,
// check if 3dm2 is installed and execute "tw_cli show" to get
// the controllers, then use the "tw_cli/cN/pN"-style drives with smartctl. This may
// happen with older smartctl which doesn't support --scan-open, or with
// drivers that don't allow proper SMART commands.
if (!multiport_found) {
debug_out_info("app", "Checking for additional 3ware devices...\n");
std::string inst_path;
hz::win32_get_registry_value_string(HKEY_USERS, ".DEFAULT\\Software\\3ware\\3DM2", "InstallPath", inst_path);
if (!inst_path.empty()) {
debug_out_dump("app", "3ware 3DM2 found at\"" << inst_path << "\".\n");
std::vector<int> controllers;
error_msg = tw_cli_get_controllers(ex_factory, controllers);
// ignore the error message above, it's of no use.
for (std::size_t i = 0; i < controllers.size(); ++i) {
// don't specify device, it's ignored in tw_cli mode
tw_cli_get_drives("", controllers.at(i), drives, ex_factory, true);
}
} else {
debug_out_info("app", "3ware 3DM2 not installed.\n");
}
}
if (!areca_open_found) {
detect_drives_win32_areca(drives, ex_factory);
if (!error_msg.empty()) {
error_msgs.push_back(error_msg);
}
}
return hz::string_join(error_msgs, "\n");
}
#endif // CONFIG_KERNEL_FAMILY_WINDOWS
/// @}
-37
View File
@@ -1,37 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#ifndef STORAGE_DETECTOR_WIN32_H
#define STORAGE_DETECTOR_WIN32_H
#include "hz/hz_config.h" // CONFIG_*
#if defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <string>
#include <vector>
#include "executor_factory.h"
#include "storage_device.h"
/// Detect drives in Windows
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
#endif
/// @}
+134 -558
View File
@@ -1,13 +1,10 @@
/**************************************************************************
Copyright:
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
Copyright:
(C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <glibmm.h> // Glib::shell_quote()
#include "rconfig/rconfig_mini.h"
#include "hz/string_algo.h" // string_trim_copy, string_any_to_unix_copy
@@ -23,136 +20,8 @@ License: See LICENSE_gsmartcontrol.txt
std::string StorageDevice::get_type_readable_name(StorageDevice::detected_type_t type)
{
switch (type) {
case detected_type_unknown:
return "unknown";
case detected_type_invalid:
return "invalid";
case detected_type_cddvd:
return "cd/dvd";
case detected_type_raid:
return "raid";
}
return "[internal_error]";
}
std::string StorageDevice::get_status_name(StorageDevice::status_t status, bool use_yesno)
{
switch (status) {
case status_enabled:
return (use_yesno ? "Yes" : "Enabled");
case status_disabled:
return (use_yesno ? "No" : "Disabled");
case status_unsupported:
return "Unsupported";
case status_unknown:
return "Unknown";
};
return "[internal_error]";
}
StorageDevice::StorageDevice(const std::string& dev_or_vfile, bool is_virtual)
{
detected_type_ = detected_type_unknown;
// force_type_ = false;
is_virtual_ = is_virtual;
is_manually_added_ = false;
parse_status_ = parse_status_none;
test_is_active_ = false;
if (is_virtual) {
virtual_file_ = dev_or_vfile;
} else {
device_ = dev_or_vfile;
}
}
StorageDevice::StorageDevice(const std::string& dev, const std::string& type_arg)
{
detected_type_ = detected_type_unknown;
// force_type_ = false;
is_virtual_ = false;
is_manually_added_ = false;
parse_status_ = parse_status_none;
test_is_active_ = false;
device_ = dev;
type_arg_ = type_arg;
}
StorageDevice::StorageDevice(const StorageDevice& other)
{
*this = other;
}
StorageDevice& StorageDevice::operator=(const StorageDevice& other)
{
info_output_ = other.info_output_;
full_output_ = other.full_output_;
device_ = other.device_;
type_arg_ = other.type_arg_;
extra_args_ = other.extra_args_;
// force_type_ = other.force_type_;
is_virtual_ = other.is_virtual_;
virtual_file_ = other.virtual_file_;
is_manually_added_ = other.is_manually_added_;
parse_status_ = other.parse_status_;
test_is_active_ = other.test_is_active_;
detected_type_ = other.detected_type_;
smart_supported_ = other.smart_supported_;
smart_enabled_ = other.smart_enabled_;
aodc_status_ = other.aodc_status_;
model_name_ = other.model_name_;
family_name_ = other.family_name_;
size_ = other.size_;
health_property_ = other.health_property_;
properties_ = other.properties_;
return *this;
}
void StorageDevice::clear_fetched(bool including_outputs) {
if (including_outputs) {
info_output_.clear();
full_output_.clear();
}
parse_status_ = parse_status_none;
test_is_active_ = false; // not sure
smart_supported_.reset();
smart_enabled_.reset();
model_name_.reset();
aodc_status_.reset();
family_name_.reset();
size_.reset();
health_property_.reset();
properties_.clear();
}
// Fetch and parse basic info
// note: this will clear the non-basic properties!
std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
if (this->test_is_active_)
@@ -160,28 +29,14 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSyn
this->clear_fetched(); // clear everything fetched before, including outputs
std::string output;
// We don't use "--all" - it may cause really screwed up the output (tests, etc...).
// This looks just like "--info" only on non-smart devices.
std::string error_msg = execute_device_smartctl("--info --health --capabilities", smartctl_ex, this->info_output_, true); // set type to invalid if needed
// Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows.
// This means that the old SCSI identify command isn't executed by default,
// and there is no information about the device manufacturer/etc... in the output.
// We detect this and set the device type to scsi to at least have _some_ info.
if (get_detected_type() == detected_type_invalid && get_type_argument().empty()) {
debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n");
this->set_type_argument("scsi");
return this->fetch_basic_data_and_parse(smartctl_ex); // try again with scsi
}
// Since the type error leads to "command line didn't parse" error here,
// we do this after the scsi stuff.
if (!error_msg.empty()) {
// Still try to parse something. For some reason, running smartctl on usb flash drive
// under winxp returns "command line didn't parse", while actually printing its name.
this->parse_basic_data(false, true);
std::string error_msg = execute_smartctl("-i -H -c", smartctl_ex, output); // --info --health --capabilities
if (!error_msg.empty())
return error_msg;
}
this->info_output_ = output;
// Set some properties too - they are needed for e.g. AODC status, etc...
return this->parse_basic_data(true);
@@ -189,6 +44,8 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSyn
// note: this will clear the non-basic properties!
std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_signal)
{
this->clear_fetched(false); // clear everything fetched before, except outputs
@@ -202,79 +59,55 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
if (!SmartctlParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all?
return "Cannot get smartctl version information.";
// Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
// Sample output line 1 (encountered on a CDRW drive):
// SMART support is: Unavailable - Packet Interface Devices [this device: CD/DVD] don't support ATA SMART
// Sample output line 2 (encountered on a BDRW drive):
// Device type: CD/DVD
if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_) || app_pcre_match("/^Device type:\\s+CD\\/DVD/mi", info_output_)) {
debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a CD/DVD device.\n");
this->set_detected_type(detected_type_cddvd);
// detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_)) {
this->set_type(type_cddvd);
// This was encountered on a csmi soft-raid under windows with pd0.
// The device reported that it had smart supported and enabled.
// Product: Raid 5 Volume
} else if (app_pcre_match("/Product:[ \\t]*Raid/mi", info_output_)) {
debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a RAID volume/controller.\n");
this->set_detected_type(detected_type_raid);
// } else {
// std::string dev_base = get_device_base();
// if (!dev_base.empty() && dev_base[0] == 'h') { // e.g. hda
// this->set_type(type_pata);
// }
}
// RAID volume may report that it has SMART, but it obviously doesn't.
if (get_detected_type() == detected_type_raid) {
// Note: We don't use SmartctlParser here, because this information
// may be in some other format. If this information is valid, only then it's
// passed to SmartctlParser.
// Compared to SmartctlParser, this one is much more loose.
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds
|| app_pcre_match("/Device Read Identity Failed \\(not an ATA\\/ATAPI device\\)/mi", info_output_)) { // solaris scsi, unsupported by smartctl
smart_supported_ = false;
smart_enabled_ = false;
} else {
// Note: We don't use SmartctlParser here, because this information
// may be in some other format. If this information is valid, only then it's
// passed to SmartctlParser.
// Compared to SmartctlParser, this one is much looser.
} else if (app_pcre_match("/^SMART support is:[ \\t]*Available/mi", info_output_)
|| app_pcre_match("/^SMART support is:[ \\t]*Ambiguous/mi", info_output_)) {
smart_supported_ = true;
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds
|| app_pcre_match("/Device Read Identity Failed/mi", info_output_)) { // solaris scsi, unsupported by smartctl (maybe others?)
smart_supported_ = false;
if (app_pcre_match("/^SMART support is:[ \\t]*Enabled/mi", info_output_)) {
smart_enabled_ = true;
} else if (app_pcre_match("/^SMART support is:[ \\t]*Disabled/mi", info_output_)) {
smart_enabled_ = false;
} else if (app_pcre_match("/^SMART support is:[ \\t]*Available/mi", info_output_)
|| app_pcre_match("/^SMART support is:[ \\t]*Ambiguous/mi", info_output_)) {
smart_supported_ = true;
if (app_pcre_match("/^SMART support is:[ \\t]*Enabled/mi", info_output_)) {
smart_enabled_ = true;
} else if (app_pcre_match("/^SMART support is:[ \\t]*Disabled/mi", info_output_)) {
smart_enabled_ = false;
}
}
}
std::string model;
if (app_pcre_match("/^Device Model:[ \\t]*(.*)$/mi", info_output_, &model)) { // HD's and cdroms
model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' ');
} else if (app_pcre_match("/^(?:Device|Product):[ \\t]*(.*)$/mi", info_output_, &model)) { // usb flash drives
} else if (app_pcre_match("/^Device:[ \\t]*(.*)$/mi", info_output_, &model)) { // usb flash drives
model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' ');
}
std::string family; // this is from smartctl's database
std::string family; // this is from smartctl's database I think
if (app_pcre_match("/^Model Family:[ \\t]*(.*)$/mi", info_output_, &family)) {
family_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(family), ' ');
}
std::string serial;
if (app_pcre_match("/^Serial Number:[ \\t]*(.*)$/mi", info_output_, &serial)) {
serial_number_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(serial), ' ');
}
std::string rpm_str;
if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", info_output_, &rpm_str)) {
int rpm = 0;
hz::string_is_numeric(rpm_str, rpm, false);
hdd_ = rpm > 0;
}
// Note: this property is present since 5.33.
std::string size;
@@ -284,23 +117,14 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
}
// Try to parse the properties. ignore its errors - we already got what we came for.
// Note that this may try to parse data the second time (it may already have
// been parsed by parse_data() which failed at it).
// try to parse the properties. ignore its errors - we already got what we came for.
if (do_set_properties) {
StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny;
if (hdd_.defined()) {
disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD;
}
SmartctlParser ps;
if (ps.parse_full(this->info_output_, disk_type)) { // try to parse it
if (ps.parse_full(this->info_output_)) { // try to parse it
this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data
}
}
// A model field (and its aliases) is a good indication whether there was any data or not
set_parse_status(model_name_.defined() ? parse_status_info : parse_status_none);
if (emit_signal)
signal_changed.emit(this); // notify listeners
@@ -309,6 +133,7 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
// fetch complete data (basic too), parse it.
std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
if (this->test_is_active_)
@@ -317,28 +142,13 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> sma
this->clear_fetched(); // clear everything fetched before, including outputs
std::string output;
std::string error_msg;
// instead of -x, we use all the individual options -x encompasses, so that
// an addition to default -x output won't affect us.
if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi
// This doesn't do much yet, but just in case...
// SCSI equivalent of -x:
error_msg = execute_device_smartctl("--health --info --attributes --log=error --log=selftest --log=background --log=sasphy", smartctl_ex, output);
} else {
// ATA equivalent of -x:
error_msg = execute_device_smartctl("--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy",
smartctl_ex, output, true); // set type to invalid if needed
}
// See notes above (in fetch_basic_data_and_parse()).
if (get_detected_type() == detected_type_invalid && get_type_argument().empty()) {
debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n");
this->set_type_argument("scsi");
return this->fetch_data_and_parse(smartctl_ex); // try again with scsi
}
// Since the type error leads to "command line didn't parse" error here,
// we do this after the scsi stuff.
// std::string error_msg = execute_smartctl("-a", smartctl_ex, output); // --all
// instead of -a, we use all the individual options -a encompasses, so that
// an addition to default -a output won't affect us.
// IDE equivalent of -a:
std::string error_msg = execute_smartctl("-H -i -c -A -l error -l selftest -l selective", smartctl_ex, output);
// SCSI equivalent of -a:
// std::string error_msg = execute_smartctl("-H -i -A -l error -l selftest", smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -348,16 +158,15 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> sma
// parses full info. if failed, tries to parse it as basic info.
// returns an error message on error.
std::string StorageDevice::parse_data()
{
this->clear_fetched(false); // clear everything fetched before, except outputs
StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny;
if (hdd_.defined()) {
disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD;
}
SmartctlParser ps;
if (ps.parse_full(this->full_output_, disk_type)) { // try to parse it (parse only, set the properties after basic parsing).
if (ps.parse_full(this->full_output_)) { // try to parse it (parse only, set the properties after basic parsing).
// refresh basic info too
this->info_output_ = ps.get_data_full(); // put data including version information
@@ -367,15 +176,13 @@ std::string StorageDevice::parse_data()
// but this one sets the StorageDevice class members, not properties.
this->parse_basic_data(false, false); // don't emit signal, we're not complete yet.
// Call this after parse_basic_data(), since it sets parse status to "info".
this->set_parse_status(StorageDevice::parse_status_full);
// set the full properties
this->set_fully_parsed(true);
this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data
signal_changed.emit(this); // notify listeners
return std::string();
return "";
}
// Don't show any GUI warnings on parse failure - it may just be an unsupported
@@ -383,24 +190,20 @@ std::string StorageDevice::parse_data()
// parsed again in Info window, and we show the warnings there.
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n");
this->set_fully_parsed(false);
// proper parsing failed. try to at least extract info section
this->info_output_ = this->full_output_; // complete output here. sometimes it's only the info section
if (!this->parse_basic_data(true).empty()) { // will add some properties too. this will emit signal_changed.
return ps.get_error_msg(); // return full parser's error messages - they are more detailed.
}
return std::string(); // return ok if at least the info was ok.
}
StorageDevice::parse_status_t StorageDevice::get_parse_status() const
{
return parse_status_;
return ""; // return ok if at least the info was ok.
}
// returns error message on error, empty string on success
std::string StorageDevice::set_smart_enabled(bool b, hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
if (this->test_is_active_)
@@ -422,7 +225,8 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
*/
std::string output;
std::string error_msg = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output);
// --smart=on --saveauto=on, --smart=off
std::string error_msg = execute_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -439,6 +243,7 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
// returns error message on error, empty string on success
std::string StorageDevice::set_aodc_enabled(bool b, hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
if (this->test_is_active_)
@@ -456,7 +261,8 @@ SMART Automatic Offline Testing Disabled.
A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.
*/
std::string output;
std::string error_msg = execute_device_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output);
// --offlineauto=on, --offlineauto=off
std::string error_msg = execute_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -472,6 +278,16 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
// get only the filename portion
std::string StorageDevice::get_virtual_filename() const
{
return (is_virtual_ ? hz::FsPath(virtual_file_).get_basename() : std::string());
}
StorageDevice::status_t StorageDevice::get_smart_status() const
{
status_t status = status_unsupported;
@@ -550,6 +366,7 @@ StorageDevice::status_t StorageDevice::get_aodc_status() const
// returns empty string on error, format size string on success.
std::string StorageDevice::get_device_size_str() const
{
return (size_.defined() ? size_.value() : "");
@@ -572,262 +389,18 @@ StorageProperty StorageDevice::get_health_property() const
std::string StorageDevice::get_device() const
{
return device_;
}
std::string StorageDevice::get_device_base() const
{
if (is_virtual_)
return "";
std::string::size_type pos = device_.rfind('/'); // find basename
if (pos == std::string::npos)
return device_; // fall back
return device_.substr(pos+1, std::string::npos);
}
std::string StorageDevice::get_device_with_type() const
{
if (this->get_is_virtual()) {
std::string ret = "Virtual";
std::string vf = this->get_virtual_filename();
ret += (" (" + (vf.empty() ? "[empty]" : vf) + ")");
return ret;
}
std::string device = get_device();
if (!get_type_argument().empty()) {
device += " (" + get_type_argument() + ")";
}
return device;
}
void StorageDevice::set_detected_type(StorageDevice::detected_type_t t)
{
detected_type_ = t;
}
StorageDevice::detected_type_t StorageDevice::get_detected_type() const
{
return detected_type_;
}
void StorageDevice::set_type_argument(const std::string& arg)
{
type_arg_ = arg;
}
std::string StorageDevice::get_type_argument() const
{
return type_arg_;
}
void StorageDevice::set_extra_arguments(const std::string& args)
{
extra_args_ = args;
}
std::string StorageDevice::get_extra_arguments() const
{
return extra_args_;
}
void StorageDevice::set_drive_letters(const std::map<char, std::string>& letters)
{
drive_letters_ = letters;
}
const std::map<char, std::string>& StorageDevice::get_drive_letters() const
{
return drive_letters_;
}
std::string StorageDevice::format_drive_letters(bool with_volnames) const
{
std::vector<std::string> drive_letters_decorated;
for (std::map<char, std::string>::const_iterator iter = drive_letters_.begin(); iter != drive_letters_.end(); ++iter) {
drive_letters_decorated.push_back(std::string() + (char)std::toupper(iter->first) + ":");
if (with_volnames && !iter->second.empty()) {
drive_letters_decorated.back() += std::string(" (") + iter->second + ")";
}
}
return hz::string_join(drive_letters_decorated, ", ");
}
bool StorageDevice::get_is_virtual() const
{
return is_virtual_;
}
std::string StorageDevice::get_virtual_file() const
{
return (is_virtual_ ? virtual_file_ : std::string());
}
std::string StorageDevice::get_virtual_filename() const
{
return (is_virtual_ ? hz::FsPath(virtual_file_).get_basename() : std::string());
}
const SmartctlParser::prop_list_t& StorageDevice::get_properties() const
{
return properties_;
}
StorageProperty StorageDevice::lookup_property(const std::string& generic_name, StorageProperty::section_t section, StorageProperty::subsection_t subsection) const
{
for (SmartctlParser::prop_list_t::const_iterator iter = properties_.begin(); iter != properties_.end(); ++iter) {
if (section != StorageProperty::section_unknown && iter->section != section)
continue;
if (subsection != StorageProperty::subsection_unknown && iter->subsection != subsection)
continue;
if (iter->generic_name == generic_name)
return *iter;
}
return StorageProperty(); // check with .empty()
}
std::string StorageDevice::get_model_name() const
{
return (model_name_.defined() ? model_name_.value() : "");
}
std::string StorageDevice::get_family_name() const
{
return (family_name_.defined() ? family_name_.value() : "");
}
std::string StorageDevice::get_serial_number() const
{
return (serial_number_.defined() ? serial_number_.value() : "");
}
bool StorageDevice::get_is_hdd() const
{
return hdd_.defined() ? hdd_.value() : false;
}
void StorageDevice::set_info_output(const std::string& s)
{
info_output_ = s;
}
std::string StorageDevice::get_info_output() const
{
return info_output_;
}
void StorageDevice::set_full_output(const std::string& s)
{
full_output_ = s;
}
std::string StorageDevice::get_full_output() const
{
return full_output_;
}
void StorageDevice::set_is_manually_added(bool b)
{
is_manually_added_ = b;
}
bool StorageDevice::get_is_manually_added() const
{
return is_manually_added_;
}
void StorageDevice::set_test_is_active(bool b)
{
bool changed = (test_is_active_ != b);
test_is_active_ = b;
if (changed) {
signal_changed.emit(this); // so that everybody stops any test-aborting operations.
}
}
bool StorageDevice::get_test_is_active() const
{
return test_is_active_;
}
std::string StorageDevice::get_save_filename() const
{
std::string model = this->get_model_name(); // may be empty
std::string serial = this->get_serial_number();
std::string date = hz::format_date("%Y-%m-%d_%H%M", true);
std::string filename_format;
rconfig::get_data("gui/smartctl_output_filename_format", filename_format);
hz::string_replace(filename_format, "{serial}", serial);
hz::string_replace(filename_format, "{model}", model);
hz::string_replace(filename_format, "{date}", date);
return hz::filename_make_safe(filename_format);
std::string filename = this->get_model_name(); // may be empty
filename += hz::format_date("-%Y-%m-%d", false);
if (!filename.empty())
filename = hz::filename_make_safe(filename) + ".txt";
return filename;
}
std::string StorageDevice::get_device_options() const
{
if (is_virtual_) {
@@ -835,32 +408,14 @@ std::string StorageDevice::get_device_options() const
return std::string();
}
// If we have some special type or option, specify it on the command line (like "-d scsi").
// Note that the latter "-d" option overrides the former.
// lowest priority - the detected type
std::vector<std::string> args;
if (!get_type_argument().empty()) {
args.push_back("-d " + get_type_argument());
}
// extra args, as specified manually in CLI or when adding the drive
if (!get_extra_arguments().empty()) {
args.push_back(get_extra_arguments());
}
// config options, as specified in preferences.
std::string config_options = app_get_device_option(get_device(), get_type_argument());
if (!config_options.empty()) {
args.push_back(config_options);
}
return hz::string_join(args, " ");
return app_get_device_option(get_device());
}
std::string StorageDevice::execute_device_smartctl(const std::string& command_options,
hz::intrusive_ptr<CmdexSync> smartctl_ex, std::string& smartctl_output, bool check_type)
// Returns error message on error, empty string on success
std::string StorageDevice::execute_smartctl(const std::string& command_options,
hz::intrusive_ptr<CmdexSync> smartctl_ex, std::string& output) const
{
// don't forbid running on currently tested drive - we need to call this from the test code.
@@ -871,22 +426,58 @@ std::string StorageDevice::execute_device_smartctl(const std::string& command_op
std::string device = get_device();
std::string error_msg = execute_smartctl(device, this->get_device_options(),
command_options, smartctl_ex, smartctl_output);
#ifndef _WIN32 // win32 doesn't have slashes in devices names
{
std::string::size_type pos = device.rfind('/'); // find basename
if (pos == std::string::npos) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n");
return "Invalid device name specified.";
}
}
#endif
if (!error_msg.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n");
if (!smartctl_ex) // if it doesn't exist, create a default one
smartctl_ex = new SmartctlExecutor(); // will be auto-deleted
// Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows.
// This means that the old SCSI identify command isn't executed by default,
// and there is no information about the device manufacturer/etc... in the output.
// We detect this and set the device type to scsi to at least have _some_ info.
if (check_type && this->get_detected_type() == detected_type_unknown
&& app_pcre_match("/specify device type with the -d option/mi", smartctl_output)) {
this->set_detected_type(detected_type_invalid);
std::string smartctl_binary = get_smartctl_binary();
if (smartctl_binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n");
return "Smartctl binary is not specified in configuration.";
}
std::string smartctl_def_options;
rconfig::get_data("system/smartctl_options", smartctl_def_options);
if (!smartctl_def_options.empty())
smartctl_def_options += " ";
std::string device_specific_options = this->get_device_options();
if (!device_specific_options.empty())
device_specific_options += " ";
smartctl_ex->set_command(Glib::shell_quote(smartctl_binary),
smartctl_def_options + device_specific_options + command_options + " " + Glib::shell_quote(device));
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Error while executing smartctl binary.\n");
// check if it's a device permission error.
// Smartctl open device: /dev/sdb failed: Permission denied
if (app_pcre_match("/Smartctl open device.+Permission denied/mi", smartctl_ex->get_stdout_str())) {
return "Permission denied while opening device.";
}
return error_msg;
return smartctl_ex->get_error_msg();
}
// any_to_unix is needed for windows
output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
}
return std::string();
@@ -894,22 +485,7 @@ std::string StorageDevice::execute_device_smartctl(const std::string& command_op
void StorageDevice::set_parse_status(parse_status_t value)
{
parse_status_ = value;
}
void StorageDevice::set_properties(const SmartctlParser::prop_list_t& props)
{
properties_ = props;
}
/// @}

Some files were not shown because too many files have changed in this diff Show More