mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 14:25:34 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af59a431b1 |
@@ -1,3 +0,0 @@
|
||||
|
||||
# Make sure github recognizes header files as C++, not C.
|
||||
*.h linguist-language=C++
|
||||
@@ -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*.*
|
||||
|
||||
@@ -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
@@ -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
|
||||
*~
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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/>
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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).
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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,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
|
||||
############################################################################
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
])
|
||||
|
||||
|
||||
@@ -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
|
||||
])
|
||||
|
||||
|
||||
@@ -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}])
|
||||
])
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
]])
|
||||
@@ -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
@@ -16,7 +16,7 @@
|
||||
# autoconf
|
||||
|
||||
|
||||
autoreconf --verbose --install -W all --force
|
||||
autoreconf --verbose --install -W all
|
||||
|
||||
|
||||
rm -f config.cache
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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";
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
@@ -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 |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -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
@@ -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
|
||||
@@ -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
@@ -1 +1 @@
|
||||
9
|
||||
5
|
||||
|
||||
+9
-6
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -85,7 +85,7 @@ binary-arch: build install
|
||||
# dh_installinit
|
||||
# dh_installcron
|
||||
# dh_installinfo
|
||||
dh_installman
|
||||
# dh_installman
|
||||
dh_link
|
||||
dh_strip
|
||||
dh_compress
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[Project]
|
||||
Manager=KDevCustomBuildSystem
|
||||
Name=gsmartcontrol
|
||||
+17
-68
@@ -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="" PATH="/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH"</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="/opt/cross-tools/bin:/target/bin:$PATH"</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="/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig" PKG_CONFIG_LIBDIR=""</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="" PATH="/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH"</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="/opt/cross-tools/bin:/target/bin:$PATH"</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="" PATH="/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH"</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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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[])
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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_, µsec);
|
||||
}
|
||||
|
||||
|
||||
/// 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_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
+155
-1142
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
/// @}
|
||||
@@ -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
|
||||
|
||||
/// @}
|
||||
@@ -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
|
||||
|
||||
/// @}
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// @}
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user