Compare commits

..
47 Commits
Author SHA1 Message Date
Alexander Shaduri ecee0047df Tagged 0.8.6. 2011-06-12 06:31:39 +00:00
Alexander Shaduri c954101bd9 Updated attribute database to smartmontools 5.41. 2011-06-11 17:27:28 +00:00
Alexander Shaduri d20aeb4121 Updated attribute DB.
Support older smartctl when filtering out 3ware sd* devices.
2011-06-07 17:08:57 +00:00
Alexander Shaduri 9440de5ca9 Updated some docs.
Proper filtering of /dev/sd* devices with older smartctl and 3ware.
Added missing file to Makefile.am.
2011-06-01 07:44:43 +00:00
Alexander Shaduri c2955b508c Fixed compilation under non-Linux/Windows platforms. 2011-05-28 12:49:56 +00:00
Alexander Shaduri fbf70814f0 Fixed make dist. 2011-05-28 12:22:54 +00:00
Alexander Shaduri a823790926 Updated NEWS file. 2011-05-28 12:18:05 +00:00
Alexander Shaduri 14838b82ae Updated readme. 2011-05-28 10:11:21 +00:00
Alexander Shaduri e11347c005 Support brief format of attributes, parse them to populate known columns. 2011-05-11 09:22:43 +00:00
Alexander Shaduri 98842f238e Windows: SUpport tw_cli/cx/px variant of smartctl device if --scan-open fails
and tw_cli is installed.
2011-05-01 14:41:58 +00:00
Alexander Shaduri 5ffc87ad9b Set tooltips wherever we have "No description available". 2011-05-01 13:03:36 +00:00
Alexander Shaduri fb5d139443 The detection now executes smartctl on the drives directly.
Don't execute smartctl --info if it was already executed during drive detection.
Made number of scanned ports configurable.
2011-05-01 12:16:26 +00:00
Alexander Shaduri 9d6ee05660 Add debian/ubuntu's su-to-root support to gsmartcontrol-root. 2011-04-30 18:41:36 +00:00
Alexander Shaduri 1ee97606aa Confirm operations (quit, rescan) when tests are running, don't just deny them. 2011-04-28 18:42:03 +00:00
Alexander Shaduri b6f721c167 Detect scsi number for 3ware and use that for tw_cli controller (Linux). 2011-04-20 12:23:54 +00:00
Alexander Shaduri ede4933819 Display device type argument along with device name everywhere. 2011-04-20 10:38:41 +00:00
Alexander Shaduri 70ce4a6d06 Fixed detecting usb flash drive names under winxp (smartctl would return a cli
parse error, which is incorrect).
Fixed parsing smartctl --scan-open output under windows.
2011-04-20 10:19:20 +00:00
Alexander Shaduri 213c26db3f Don't add sd* device if it's 3ware controller.
Properly add devices detected through 3ware scan (linux).
Limit smartctl sequential scans to 24 drives (it takes too long without it).
2011-04-19 17:59:44 +00:00
Alexander Shaduri 8ae3c42100 Added tooltips to both the labels and the entries in Add Device and Preferences
dialogs.
Changed the tooltips to avoid showing unsupported values for that OS.
Fixed usb drive re-detection with scsi (regression from previous commit).
Fixed invalid regexps in tw_cli output parsing (fixes 3ware in windows).
Added support for "Product" field of USB drives (used instead of Device in
newer smartctl).
2011-04-18 12:14:20 +00:00
Alexander Shaduri ff466a1d2d Add a note that we probably support Intel Matrix controllers as well. 2011-04-17 20:55:50 +00:00
Alexander Shaduri 5e46ed7cb6 Added support for devices detected by "smartctl --scan-open" under win32 (this
includes 3ware).
2011-04-17 20:36:54 +00:00
Alexander Shaduri c197ba76fa Support detecting drives behind 3ware controllers.
Add ExecutorFactory - now detectors can create their own gui or non-gui
executors, depending on who called them.
2011-04-17 18:47:43 +00:00
Alexander Shaduri fa2b10f3ce Show drive WWN and sector size. 2011-04-17 09:28:59 +00:00
Alexander Shaduri 7b523aa075 Updated to latest drivedb.h.
Made the type entry in Add Device window an editable combo, added sample types.
2011-04-16 20:38:39 +00:00
Alexander Shaduri 0ca9971460 Some more UI tweaks.
A new "Add Device" dialog - allows specifying type and additional smartctl
parameters.
Drive properties in preferences - allow specifying device type.
2011-04-16 19:25:06 +00:00
Alexander Shaduri b3f23abec0 Made the user interface more GNOME HIG-compliant. 2011-04-16 11:29:46 +00:00
Alexander Shaduri a2d735fee5 Added support for specifying -d option and extra parameters via command line
and preferences dialog. StorageDevice will use the specified type and
arguments when executing smartctl. This change effectively adds support for
multiple drives behind a single device name.
The preferences dialog can distinguish between various device types now (when
supplying smartctl arguments).
2011-04-04 20:02:41 +00:00
Alexander Shaduri fb801d8ebb Documented, reorganized StorageDevice source code.
Added "type argument" and "extra arguments" fields to StorageDevice.
2011-04-04 18:22:18 +00:00
Alexander Shaduri 9a92bfec45 Renamed "type" field (and all related stuff) of StorageDevice to detected_type. 2011-04-04 16:46:28 +00:00
Alexander Shaduri 6d50abb3cf Detect one more "smart unsupported" string in the latest version of smartctl
output.
Add a hint about "pd0" for Windows version in Preferences dialog.
2011-04-04 16:26:10 +00:00
Alexander Shaduri 778872363a New and improved attribute database (including SSD attributes).
Attributes are now matched according to smartctl names, and only then by ids.
Attribute notices are set by their generic names, not ids.
Improved displaying of attributes when they're unknown to us but known to
smartctl.
Set descriptions on all known capabilities.
Disabled tooltips on self-test log and error log entries (they always showed
"description unavailable" anyway).
2011-03-12 21:06:15 +00:00
Alexander Shaduri 6c404d57c9 Use -include only when using pch.
Document OptionalValue.
Use spaces when formatting date/time.
Capitalize some property names for consistency.
2011-03-09 11:26:52 +00:00
Alexander Shaduri 449b645278 Split storage detector to per-platform files. 2011-03-06 21:12:18 +00:00
Alexander Shaduri 02428a26c0 Show Error Log and Self-Test Log support.
Support attributes with undefined values like "---".
2011-03-06 20:31:39 +00:00
Alexander Shaduri 90bbc19923 Fixed inability to parse some error types in error log. 2011-03-06 19:04:13 +00:00
Alexander Shaduri 4f5690c89b Added ability to show smartctl output for devices whose info could not be
parsed fully.
2011-03-06 18:06:14 +00:00
Alexander Shaduri 4000fa1a2e Added the ability to configure default smartctl info save filename through
config file.
Use model-serial-date.txt for default save filename.
2011-03-06 17:15:56 +00:00
Alexander Shaduri ba7ffafa7f Updated copyrights to 2011.
Adjust Makefile.am for dist-files.
Added configure-dev script for easy developer-build configuration.
2011-03-06 10:43:38 +00:00
Alexander Shaduri 33b59d5723 Updated copyrights to 2010.
Reimplement locale stuff in hz (adding different controls for C and C++
locales), also take the recent locale-related changes in gtkmm into account.
Cleanups in snprintf-related code, portable_snprintf().
Added doxygen-style comments to some hz headers.
Better console redirection handling for win32.
Blacklisted linux device mapper devices (annoying in LVM setups).
Added beesu support and generally improved gsmartcontrol-root script (this is
needed for Fedora 14 which doesn't have any old supported su mechanisms).
Added "menu" dependency for deb packages.
Added kdev4 project.
2010-12-26 20:35:24 +00:00
Alexander Shaduri 28551a9666 Note: This revision was used to build 0.8.5-4 Windows package.
Set C++ locale as well, when setting C locale.
Improved windows installer.
2010-12-26 20:12:08 +00:00
Alexander Shaduri c5478d6a0e Note:This revision was used to build 0.8.5-3 Windows package.
Previous test result is cleared in GUI when running new tests now.
Windows: Vastly improved installer, supports built-in GTK+ now.
Windows: A proper method of privilege escalation is used for Vista/7.
Windows: GSmartControl works on Windows 7 now.
Updated copyright for files with Whatever License to use Unlicense.
2010-12-26 20:07:02 +00:00
Alexander Shaduri 2ca8174054 Importing 0.8.5. 2010-12-26 19:50:52 +00:00
Alexander Shaduri 10e4496bb7 Importing 0.8.4. 2010-12-26 19:46:54 +00:00
Alexander Shaduri 880d876ccf Importing 0.8.4-pre-2009-03-21. 2010-12-26 19:44:01 +00:00
Alexander Shaduri 2ffec89ed8 Importing 0.8.3 2010-12-26 19:40:08 +00:00
Alexander Shaduri 5c257ffdc4 Tagging 0.8.2. 2010-12-26 19:33:25 +00:00
Alexander Shaduri dc6297b0c9 Importing 0.8.1 2010-12-26 19:24:29 +00:00
281 changed files with 15300 additions and 4494 deletions
+1 -1
View File
@@ -13,4 +13,4 @@ install-sh
*.glade.cpp
*.ui.cpp
*.txt.cpp
gsmartcontrol.spec
.kdev4
+25
View File
@@ -0,0 +1,25 @@
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.
+3 -2
View File
@@ -18,8 +18,9 @@ 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, tests and examples are
covered under the Whatever License. See LICENSE_whatever.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.
Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt
+6 -2
View File
@@ -9,14 +9,18 @@ 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 Whatever License. See
LICENSE_whatever.txt for details.
All tests and examples are covered under the Unlicense. See
LICENSE_unlicense.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.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2005, Google Inc.
Copyright (c) 2005 - 2010, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
+25
View File
@@ -0,0 +1,25 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
-12
View File
@@ -1,12 +0,0 @@
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.
+96 -6
View File
@@ -2,7 +2,7 @@
# Add autoconf.m4 directory to local macro search path
ACLOCAL_AMFLAGS = -I autoconf.m4
SUBDIRS = src data
SUBDIRS = data debian.dist src
# These files are actually needed for compilation.
@@ -16,21 +16,28 @@ noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt README.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_pcrecpp.txt \
LICENSE_whatever.txt \
LICENSE_unlicense.txt \
LICENSE_zlib.txt
# nobase_ preserves their directory names.
nobase_dist_doc_DATA = contrib/cron-based_noadmin/README \
contrib/cron-based_noadmin/cron_gather_smart.sh \
contrib/cron-based_noadmin/crontab.example \
contrib/cron-based_noadmin/smartctl_subst.sh
# Extra files bundled with distribution.
# file2csource.sh is needed for building but not installed.
EXTRA_DIST = COPYING TODO INSTALL configure autogen.sh \
baqup gsmartcontrol.kdevelop \
templates \
EXTRA_DIST = COPYING INSTALL configure autogen.sh \
gsmartcontrol.kdev4 \
file2csource.sh \
gsmartcontrol.spec
@@ -46,7 +53,6 @@ gch: global_macros.h.gch
gch-clean:
rm -f global_macros.h.gch
distclean-local: gch-clean
# rpm support
@@ -60,3 +66,87 @@ src-rpm: dist
# rpmbuild -ta $(distdir).tar.bz2
# NSIS installer support.
# Requires wine, installed NSIS, smartctl-nc.exe, smartctl.exe.
# Gtkmm dlls and pcre dlls are required if linking against them.
# dos2unix on build machine.
# Execute only with win32 build present.
WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-win32
WIN_ZIP_NOGTK_NAME = $(PACKAGE)-$(VERSION)-win32-nogtk
win-dist: win-dist-nocleanup win-dist-cleanup
win-dist-nocleanup: win-dist-prepare nsis-dist-nocleanup win-zip-dist-nocleanup
win-dist-cleanup: win-zip-dist-cleanup nsis-dist-cleanup
rm -rf win-dist
win-dist-clean: win-dist-cleanup
rm -f gsmartcontrol-*.exe gsmartcontrol-*.zip
win-dist-prepare-all: nsis-dist-prepare win-zip-dist-prepare
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
cp "@WINDOWS_BINDIR@"/smartctl-nc.exe win-dist/
cp "@WINDOWS_BINDIR@"/smartctl.exe win-dist/
cp src/gsmartcontrol.exe src/gsmartcontrol.exe.manifest win-dist/
unix2dos win-dist/doc/*.txt
$(STRIP) win-dist/*.exe
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
cp "@WINDOWS_BINDIR@"/"@GTK_INSTALLER_NAME@".exe nsis-dist/
nsis-dist-nocleanup: nsis-dist-prepare
cd nsis-dist && @NSIS_EXEC@ gsmartcontrol.nsi
mv nsis-dist/$(PACKAGE)-$(VERSION).exe .
cd nsis-dist && @NSIS_EXEC@ /DNO_GTK gsmartcontrol.nsi
mv nsis-dist/$(PACKAGE)-$(VERSION)-nogtk.exe .
nsis-dist-cleanup:
rm -rf nsis-dist
nsis-dist: nsis-dist-nocleanup nsis-dist-cleanup
win-zip-dist-prepare:
cp -a win-dist $(WIN_ZIP_NAME)
cp -a win-dist $(WIN_ZIP_NOGTK_NAME)
cp -a "@WINDOWS_BINDIR@"/"@GTK_INSTALLER_NAME@"/* $(WIN_ZIP_NAME)/
win-zip-dist-nocleanup: win-zip-dist-prepare
zip -K -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME) -x $(WIN_ZIP_NAME)/doc/distribution-nogtk.txt
zip -K -9r $(WIN_ZIP_NOGTK_NAME).zip $(WIN_ZIP_NOGTK_NAME) -x $(WIN_ZIP_NOGTK_NAME)/doc/distribution.txt
win-zip-dist-cleanup:
rm -rf $(WIN_ZIP_NAME)
rm -rf $(WIN_ZIP_NOGTK_NAME)
win-zip-dist: win-zip-dist-nocleanup win-zip-dist-cleanup
distclean-local: gch-clean win-dist-clean
+99 -1
View File
@@ -1,10 +1,108 @@
Version 0.8.6, released on 2011-05-29
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).
Attributes tab is before the capabilities tab now.
A man page has been generously contributed by Giuseppe Iuculano
<giuseppe@iuculano.it>.
Smartctl version now includes the CVS snapshot date (if available).
Windows: Look for "smartctl-nc.exe" instead of "smartctl.exe" by default.
Windows: Use smartmontools-supplied smartctl-nc.exe by default (if found).
Other minor changes (mainly Debian backports).
Version 0.8.3, released on 2008-12-27
A random "Smartctl returned an empty output" error on Windows was fixed.
Thanks to Zurab Khetsuriani for testing.
Fixed a parser issue which prevented running self-tests in Windows.
The supplied icon (hopefully) shows correctly in Windows 2000 now.
This release adds an official support for Windows 2000 SP4.
Added scripts to allow GSmartControl to read smartctl data from
cron-generated files. This allows users to read somewhat recent
smartctl information without having to run GSmartControl as root.
Generously contributed by Alex Butcher <alex dot butcher 'at'
assursys.co.uk>.
Configure script correctly aborts instead of printing a warning if gtkmm
or libglademm (if needed) is not found.
Configure script now accepts --enable-windows-console,
--disable-abort-if-no-gtkmm, --disable-abort-if-no-glade-reader,
as well as Windows-supporting "auto" for --enable-nsis-wine and
--with-nsis.
Configure's --with-win32-env has been renamed to --with-windows-dlls.
The "About" dialog shows version information now.
Minor bugs were fixed.
Version 0.8.2, released on 2008-12-10
Fixed gsmartcontrol_root.sh script to support distributions with no
/usr/sbin in their users' paths (thanks to Erwan Velu).
Added desktop auto-detection to gsmartcontrol_root.sh script. This allows
us to use only one desktop file (thanks to Erwan Velu).
Added Debian package directory (named "debian.dist" for now to avoid
control file conflicts with Build Service).
Renamed gsmartcontrol_root.sh to gsmartcontrol-root, to make Debian happy.
Added make targets for Windows packages (zip and NSIS).
Fixed Windows-related issues (Vista is fully supported now).
Fixed minor bugs:
Smartctl parser is win32-locale-aware now.
No more unnecessary parsing.
No segfault on exit under Windows and Solaris.
A friendlier message is displayed if smartctl was not found.
No highlighted labels when switching tabs in Information window.
Version 0.8.1, released on 2008-11-11
Disabled Linux "by-id" drive detection - it's unreliable on some broken
systems.
Added some more attribute descriptions.
Our names for attributes override smartctls' now.
Added a proper "Add Device" dialog for Windows.
Added an icon and resource file for Windows.
Fixed minor bugs.
Version 0.8.0rc4, released on 2008-10-20
FreeBSD support (tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86).
NetBSD support (tested with NetBSD 4.0.1 / x86).
OpenBSD support (tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5).
Solaris/gcc support (tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave).
Solaris/sunstudio support (tested with Solaris 10 / x86 / sunstudio12 / sunfreeware).
Solaris/sunstudio support (tested with Solaris 10 / x86 / sunstudio12 /
sunfreeware).
Code to support Windows, Mac OS X, QNX.
Support of older pcre versions (at least 4.5, maybe older too).
Added gsmartcontrol_root.sh script to easily run gsmartcontrol as root.
+121 -56
View File
@@ -10,7 +10,8 @@ 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.
Note: Only ATA drives (both PATA and SATA) are supported for now.
Note: GSmartControl supports ATA drives (both PATA and SATA), various USB to
ATA bridges and drives behind 3ware RAID controllers.
http://gsmartcontrol.berlios.de
@@ -18,30 +19,31 @@ http://gsmartcontrol.berlios.de
Features
Automatically report and hilight any abnormal SMART information.
* automatically reports and highlights any anomalies;
Ability to enable / disable SMART.
* allows enabling/disabling SMART;
Ability to enable / disable Automatic Offline Data Collection - A short
* 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.
impact on performance;
Ability to set global and per-drive options for smartctl.
* supports configuration of global and per-drive options for smartctl;
Display drive identity, capabilities, attributes, error and self-test logs.
* performs SMART self-tests;
Perform SMART self-tests.
* displays drive identity information, capabilities, attributes, and
self-test/error logs;
Ability to load smartctl output as a "virtual" device, which acts just like a
real (read-only) device.
* can read in smartctl output from a saved file, interpreting it as a
read-only virtual device;
Works on most smartctl-supported operating systems.
* works on most smartctl-supported operating systems;
Extensive help information.
* has extensive help information.
What is SMART?
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.
@@ -60,7 +62,7 @@ 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 commond pitfalls is that it may create a false sense of security. That
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
@@ -99,16 +101,36 @@ You need to have the following software installed:
* Gtkmm, version 2.6 or higher - see http://www.gtkmm.org .
* libglademm, version 2.4 or higher - see http://www.gtkmm.org .
libglademm is not needed when using GTK 2.12 and Gtkmm 2.12.
Note: libglademm is not needed when using GTK / Gtkmm 2.12 or newer.
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.
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.
Note that GTK+ 2.12 and Gtkmm 2.12 are HIGHLY recommended. While earlier
versions may work, they may produce ugly results and buggy behaviour.
Libglademm is not needed when using GTK / Gtkmm 2.12.
Most of these packages are probably already provided by your distribution. For
example, to build this program on OpenSUSE, you just need to install
smartmontools, gtkmm2-devel and pcre-devel - the rest is installed
automatically by the package manager's dependency resolver.
The following operating systems are supported:
@@ -120,36 +142,40 @@ The following operating systems are supported:
* OpenBSD - Tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5.
* Windows (2000 SP4 or newer) - Tested with Windows 2000 SP4, Windows XP SP3,
Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1 (32-bit and 64-bit). 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.
not been tested yet. Note: smartctl doesn't seem to support ATA under Solaris,
while GSmartControl doesn't support SCSI yet. This only leaves various USB to
ATA bridges. See smartctl (5), -d option for details.
* 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 (NT line only) - Only minimal testing has been performed. Expected
to work. The Windows port uses /dev/pd0, /dev/pd1, etc... for physical drives
0, 1, etc... .
* Mac OS X - Code written but no testing has been performed yet.
* QNX - Code written but no testing has been performed yet.
Installation
Short answer: ./configure; make; make install
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
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
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
@@ -157,15 +183,53 @@ dependencies (see Software Requirements section). Then the usual
./configure; make; make install
will build and install it. Installation usually requires adminstrative
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 compilation directory.
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. 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.
-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.
--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'
-v, --verbose - Enable verbose logging; same as --verbosity-level 5.
-q, --quiet - Disable logging; same as --verbosity-level 0.
-b, --verbosity-level - Set verbosity level [0-5].
Permission Problems
Short answer: you need to be root (Administrator in Windows).
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.
@@ -179,8 +243,8 @@ 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.sh script, which finds
the available su program and runs gsmartcontrol with it.
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,
@@ -210,9 +274,10 @@ 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 his/her boot.local, rc.local or similar:
the following to the system startup script (e.g. boot.local, rc.local or
similar on Linux):
smartctl --smart=on --offlineauto=on /dev/sda
smartctl -s on -o on /dev/sda
For more information, see smartctl and smartd documentation.
@@ -220,12 +285,12 @@ 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:
Only ATA drives (both PATA and SATA), various USB to ATA bridges and drives
behind 3ware RAID controllers 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...).
some 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
@@ -234,19 +299,14 @@ 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.
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.
monitor them yet. Besides, they run automatically anyway if Automatic Offline
Data Collection is enabled.
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.
they report otherwise. Unfortunately, there's no way to detect such drives.
Running on GTK+ / Gtkmm versions earlier than 2.12 may cause visual artifacts,
usability issues (especially with tooltips and icons), instability, etc...
@@ -266,14 +326,17 @@ 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 operating system you use (for example, openSUSE Linux 11.4).
* Which version of GTK and Gtkmm you have installed. Finding this out is very
distribution-specific. For example, on OpenSUSE it would be
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.
@@ -281,7 +344,9 @@ search them in your distribution's graphical package manager, if there is one.
with -v option, e.g. (type the following in a terminal emulator or Run
dialog):
gsmartcontrol_root.sh other -v
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".
@@ -305,7 +370,7 @@ 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-existant licenses, whatever anyone else
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.
+168 -70
View File
@@ -16,26 +16,19 @@ local scope destruction also works if using stack variables.
A treeview (also iconview, combobox) from glade comes
with existing model, which should be unset
with existing model, which should be unset.
--------------------------------------------------------
Bugs / patches:
portability:
win32 32-bit: int: 4, long: 4, pointer: 4.
win32 64-bit: int: 4, long: 8, pointer: 8.
Patch pangomm for Pango::strip_markup().
sparc: same as win32 (what about OS?).
+ gtkmm bug: No Gtk::AboutDialog::set_logo(NULL);
--------------------------------------------------------
GTKMM problems:
Lots of gtk_, GTK_, g_ in documentation.
no ComboBox::unset_model() and IconView::unset_model().
+ Gtk::Main m(argc, argv, false);
If gtk_get_option_group() and g_option_context_parse() were used before,
this generates a warning:
(gsmartcontrol:28241): Gtk-WARNING **: gtk_disable_setlocale() must be called before gtk_init()
@@ -44,78 +37,183 @@ no ComboBox::unset_model() and IconView::unset_model().
TODO:
test, release RC version for testing.
Update to drivedb.h from 5.41, include 5.41 into release.
Testing:
If ETA time has elapsed, but it's still only at 10% completion,
ETA 0 is displayed. Fix.
If smartctl -t ... returns an error, testing=true should not be set.
Can't reproduce it?
Detect running tests on launch (maybe ask the user too? some tests
may be stuck due to bad firmware, e.g. 3ware/windows).
Add support for launching with consolehelper (from usermode package).
Fedora uses this by default.
Investigate Fedora selinux situation.
usermode-gtk contains consolehelper-gtk, not sure about our package
requirement. consolehelper-gtk is not supposed to be launched by user,
but I think we can check it with "which".
http://fedoranews.org/contributors/matt_hansen/nautilus/
Rework drive properties UI:
use 48-bit or not or auto.
Enable/disable/dont_touch SMART on startup for this drive.
Enable/disable/dont_touch AODC on startup for this drive.
Add --init-smart-only option to only apply the SMART/AODC enabling status
from the drive properties.
Add also --no-init-smart to avoid doing that on GUI startup.
Notifications:
Tied to (drive::subdrive, notif_key, exact_value(optional)).
If exact_value isn't specified when ignoring, notify on every value change.
Ignore options:
Per-drive: Ignore all notifications for this key, ignore only this value.
Don't generate multiple notifications for the same drive/key combo.
Clearing in preferences: Clear all ignored, clear ignored for this drive, clear individual.
Soft-warnings require "-a" by default, tricky for scsi (maybe try -a, then -i?).
Monitor:
Needs root access, so not gonna happen until the dbus/udisks stuff
is finalized.
Same program, add --tray option to start in tray only.
Option to enable the tray icon.
Option to continue running in the tray on window close.
Use freedesktop notifications when notifications are generated
and we're running in tray only.
Don't generate the same notification at a single run time if it was
ignored (untouched) by the user; Change the tray icon and
show them in the main window instead.
Periodically re-run smartctl to refresh the notifications.
Periodically see if drives were added / removed (possibly use OS callbacks
instead of polling?)
If a drive is not recognized, suggest using -d (sat, sat,12, ...)
Refer the user to
http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_USB-Devices
RAID:
http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_RAID-Controllers
Areca Linux:
Call as: smartctl -i -d areca,[1-24] /dev/sg0
Detection:
Check if /proc/scsi/sg/device_strs contains "Areca RAID controller".
If not, there's no Areca controller present.
Check /proc/scsi/sg/devices, the line # with type 3 is the sg* number.
The "host" field value is the N value in /sys, see below.
Check /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_hd_channels, set
its contents as the number of ports. If not present, use 24.
Probe each port for a valid drive: If the output contains "Device Read Identity Failed",
then there is no drive on that port (or Areca has an old firmware, nothing we can
do there).
Notification: If /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_version
is older than "V1.46 2009-01-06", notify the user (maybe its better to grep
the smartctl output for that on port 0?).
3ware FreeBSD:
Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0)
Detection: unknown.
LSI MegaRAID (SCSI/SAS), Linux:
Call as: smartctl -i -d megaraid,[0-?] /dev/sda
We should differentiate between ordinary sda and RAID one (not sure if sda is detected).
Detection: unknown.
HighPoint RocketRAID ((S)ATA) Linux:
Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/sda
Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/sda
Note: /dev/sda should be derived from this controller.
We should differentiate between ordinary sda and RAID one (not sure if sda is detected).
Smartctl docs specify smaller limits, but the code says these are the ones.
Detection: unknown.
HighPoint RocketRAID ((S)ATA) FreeBSD:
Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/hptrr
Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/hptrr
Note: /dev/hptrr should be derived from this controller.
Smartctl docs specify smaller limits, but the code says these are the ones.
Detection: unknown.
TODO: Analyze sent info.
CCISS (HP (Compaq) Smart Array Controller) Linux:
Call as: smartctl -a -d cciss,[0-127] /dev/cciss/c0d0
c0d0 is the controller (d0 seems to be a logical drive, doesn't matter?).
/dev/cciss/c0d0p1 is the first ordinary partition (used in mount, etc...).
The port limit had a brief regression in 5.39.x (limited to 15).
5.39 doesn't seem to work at all (prereleases work with P400 controller)?
Possibly a 64-bit-only issue?
To detect controller presence: cat /proc/devices, contains "cciss0"
for controller 0.
There is a cciss_vol_status utility but it doesn't report anything except "OK" status.
It may not return the "SMART Health Status: OK" line in smartctl -i for some reason?
(It returns it in -a though, and in some freebsd output I found).
If no /dev/cciss exists, you may need to run "cd /dev; ./MAKEDEV cciss".
Smartctl SVN has some /dev/sg* stuff, check it out.
CCISS (HP (Compaq) Smart Array Controller) FreeBSD:
Seems to be the same as Linux (uses the same driver?).
Detection: unknown.
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).
use format_time() instead of ctime() and friends, with ISO formatting.
48-bit ATA command support:
'-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 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.
"smartctl -l this_wont_exist" gives the list of supported logs - this
is needed to detect xerror / xselftest support in smartctl.
'-l sataphy' -> SATA Phy event counters: Non-SMART feature, useful to detect SATA cabling problems.
----------- 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).
Which one is updated more frequently, SCT or attr 194?
+ * 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 (do we use these?)
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.
-l scterc[,...] to get/set the SCT Error Recovery Control time limit (5.40 and newer).
+Tests:
Ability to detect a testing drive on boot, show its info with testing tab.
Add detection for Interix (i586-pc-interix3), maybe with debian?
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.
----------- SMART stuff ----------
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
+19 -8
View File
@@ -26,12 +26,23 @@
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,,[define if the compiler supports exceptions])
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, 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
])
+18 -8
View File
@@ -26,12 +26,22 @@
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,,[define if the compiler implements namespaces])
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, 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
])
+31 -21
View File
@@ -26,25 +26,35 @@
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,,
[define if the compiler supports Run-Time Type Identification])
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, 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
])
+21 -12
View File
@@ -25,16 +25,25 @@
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, , [define if the compiler has __gnu_cxx::__verbose_terminate_handler])
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, 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
])
+1 -1
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
+37 -14
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -22,6 +22,12 @@ 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()
@@ -65,8 +71,24 @@ AC_DEFUN([APP_CHOOSE_GLD_READER], [
# 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
PKG_CHECK_MODULES([app_cv_gld_reader], [$app_cv_gld_reader_libglade_pkg_name >= 2.4.0],
[app_cv_gld_reader="libglade"], [AC_MSG_WARN([$LIBGLADE_PKG_ERRORS])])
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
@@ -75,16 +97,13 @@ AC_DEFUN([APP_CHOOSE_GLD_READER], [
# still not found.
if test "$app_cv_gld_reader" = "none"; then
AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found. The program will be unable to compile fully.])
fi
# pkg-config doesn't do CXXFLAGS, so do it manually.
app_cv_gld_reader_CXXFLAGS="$app_cv_gld_reader_CFLAGS";
# if using libglademm, it doesn't make any sense to leave the flags for C.
if test "x$1" = "xC++"; then
app_cv_gld_reader_CFLAGS="";
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
@@ -116,13 +135,17 @@ AC_DEFUN([APP_CHOOSE_GLD_READER], [
# export to config.h
if test "$app_cv_gld_reader" = "libglade"; then
AC_DEFINE(ENABLE_LIBGLADE, 1, [Use libglade(mm) instead of GtkBuilder])
else
AC_DEFINE(ENABLE_LIBGLADE, 0, [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])
else
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_CHECKING([for glade file support])
AC_MSG_RESULT([$app_cv_gld_reader])
+54 -15
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -50,19 +50,17 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# gcc / gnu environment, mingw, cygwin.
if test "x$app_cv_compiler_common_options" = "xgnu"; then
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; 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
# mingw gcc options:
# -mno-cygwin - generate non-cygwin executables in cygwin's mingw.
# -mwindows - hide console window. possibly suppresses any std output / error.
# -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mms-bitfields -mwindows"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mms-bitfields -mwindows"
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mms-bitfields -mwindows"
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
# Note: Disabled -Wconversion, it was causing lots of silly warnings under x86-64.
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual \
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual -Wconversion \
-Wctor-dtor-privacy -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
-Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings";
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags $app_cv_compiler_tmp_var";
@@ -80,7 +78,16 @@ 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/819-5267/6n7c46drp?a=view
# 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
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 \
@@ -167,11 +174,11 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# gcc, mingw
if test "x$app_cv_compiler_optimize_options" = "xgnu"; then
# -mtune=generic is since gcc 4.0 iirc
if test "$app_cv_target_os_env" = "mingw" || test "$app_cv_target_os_env" = "cygwin"; 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
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 -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";
@@ -214,6 +221,38 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [
# ---- Compiler options for Mingw's mwindows/mconsole
# -mwindows - hide console window. possibly suppresses any std output / error.
# -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. ]
[Auto means disabled for optimized builds, enabled for all others. (Default: auto)]),
[app_cv_compiler_windows_console=${enableval}], [app_cv_compiler_windows_console=auto])
if test "x$app_cv_compiler_windows_console" = "xauto"; then
# disable console for optimized builds.
if test "x$app_cv_compiler_optimize_options" != "xnone"; then
app_cv_compiler_windows_console="no";
else
app_cv_compiler_windows_console="yes";
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_compiler_windows_console" = "xno"; then
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_ldflags="$app_cv_compiler_options_ldflags -mconsole";
fi
AC_MSG_NOTICE([Enable windows console: $app_cv_compiler_windows_console])
fi
# ---- Export our variables
if test "x$$1[]CFLAGS" = "x"; then
+11 -6
View File
@@ -1,13 +1,14 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports overloading between
# extern "C" function pointer and C++ function pointer. GCC and
# 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
# Intel treat them equally, so no overloads are needed (or accepted)
# there.
@@ -21,7 +22,8 @@
# for template parameters (I don't remember the exact details).
# If such support is found, HAVE_CXX_EXTERN_C_OVERLOAD is defined.
# If such overloading support is found,
# HAVE_CXX_EXTERN_C_OVERLOAD is defined.
AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [
@@ -37,8 +39,11 @@ 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, ,
[Define if the C++ complier accepts extern C function pointer overload])
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])
fi
])
+12 -6
View File
@@ -1,14 +1,14 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 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,8 +17,11 @@ AC_DEFUN([APP_CXX___FUNC__], [
AC_LANG_POP([])
])
if test "x$app_cv_cxx___func__" = "xyes"; then
AC_DEFINE(HAVE_CXX___func__, ,
[Define if the C++ complier supports __func__])
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])
fi
])
@@ -35,8 +38,11 @@ AC_DEFUN([APP_CXX___FUNCTION__], [
AC_LANG_POP([])
])
if test "x$app_cv_cxx___FUNCTION__" = "xyes"; then
AC_DEFINE(HAVE_CXX___FUNCTION__, ,
[Define if the C++ complier supports __FUNCTION__])
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])
fi
])
+65 -13
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -59,12 +59,26 @@ AC_DEFUN([APP_DETECT_OS_KERNEL], [
app_cv_[]$1[]_os_kernel_macro="LINUX";
;;
*mingw* | *cygwin*)
i*86-*mingw* | i*86-*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="windows";
app_cv_[]$1[]_os_kernel_macro="WINDOWS";
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";
;;
# This includes debian gnu/kfreebsd. Dragonfly is checked separately.
@@ -124,8 +138,14 @@ 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}" = "xwindows"; then
AC_DEFINE($2[]WINDOWS, 1, [$1 OS kernel])
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])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS kernel])
@@ -154,7 +174,9 @@ 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_WINDOWS, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows"])
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_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"])
@@ -164,6 +186,19 @@ 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}])
])
@@ -216,9 +251,17 @@ AC_DEFUN([APP_DETECT_OS_ENV], [
app_cv_[]$1[]_os_env="cygwin";
app_cv_[]$1[]_os_env_macro="CYGWIN";
;;
*mingw*)
app_cv_[]$1[]_os_env="mingw";
app_cv_[]$1[]_os_env_macro="MINGW";
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";
;;
# This includes gnu/linux, debian gnu/kfreebsd, debian gnu/netbsd...
*-gnu | gnu*)
@@ -271,8 +314,14 @@ 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}" = "xmingw"; then
AC_DEFINE($2[]MINGW, 1, [$1 OS userspace environment])
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])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xgnu"; then
AC_DEFINE($2[]GNU, 1, [$1 OS userspace environment])
@@ -304,7 +353,9 @@ 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_MINGW, [test "x${app_cv_[]$1[]_os_env}" = "xmingw"])
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_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"])
@@ -315,6 +366,7 @@ 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}])
])
+56 -19
View File
@@ -1,14 +1,14 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 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
# (possibly pthreads-based) applications on target OS. flags_prefixCFLAGS,
# (preferably 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 indicate thread-safe support to standard libs:
# Compilers use at least 3 defines to enable thread-safe support:
# _THREAD_SAFE, _MT, _REENTRANT.
# We use the ones that are required for detected platform.
@@ -63,17 +63,20 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
app_cv_target_thread_found="yes";
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# suncc needs -lpthread on posix but not solaris.
# -mt defines _REENTRANT too. Don't use -lthread.
# 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
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt -lpthread"
app_cv_target_thread_libs="-mt"
app_cv_target_thread_found="yes";
fi
;;
windows)
windows*)
# NOTE: Not sure about cygwin.
# NOTE: This only enables multithreaded code generation. For linking
# with pthreads-win32 you need additional flags.
@@ -87,10 +90,20 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
fi
;;
freebsd | dragonfly | netbsd | openbsd)
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)
# NOTE: Not sure about debian gnu/kfreebsd and gnu/netbsd.
# Freebsd had -kthread, but it has been discontinued (afaik).
# For freebsd -pthread already replaces libc with libc_r.
# For freebsd -pthread 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"
@@ -99,22 +112,48 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
fi
;;
openbsd)
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; 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"; 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"; 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"
# 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"
app_cv_target_thread_libs="-pthreads"
app_cv_target_thread_found="yes"
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# -mt defines _REENTRANT too. Don't use -lthread.
# 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
# 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
# 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"
# 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"
app_cv_target_thread_found="yes"
fi
;;
@@ -122,8 +161,6 @@ AC_DEFUN([APP_GET_MT_FLAGS], [
darwin)
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
;;
+45 -3
View File
@@ -1,7 +1,7 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2009 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
@@ -13,6 +13,9 @@
# 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.
@@ -66,10 +69,49 @@ 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
# mistakenly assumes that __EXTENSIONS__ is the one, but
# it should be defined _in addition_ to feature test macros.
# 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.
# 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
;;
mingw32)
# 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=0x0500"
app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0500"
;;
mingw64)
# Same as ming32, but enable "from xp" mode, since there's no x86_64 2k.
app_cv_target_extension_cflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0501"
app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0501"
;;
interix)
# As per AC_USE_SYSTEM_EXTENSIONS.
app_cv_target_extension_cflags="-D_ALL_SOURCE"
app_cv_target_extension_cxxflags="-D_ALL_SOURCE"
;;
esac
+32 -21
View File
@@ -28,29 +28,40 @@
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>
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 */
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);
std::string name(c_name);
free(c_name); /* not std::free() */
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
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
])
Executable
+70
View File
@@ -0,0 +1,70 @@
#!/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 "debug optimized icc win32 win32-debug win64-debug sun portland open64";
echo "<configure_script> is ../configure by default"
exit 1;
fi
if [ "$configure_script" == "" ]; then
configure_script="../configure";
fi
flags="";
case $type in
optimized)
flags="--disable-user-flags --enable-optimize-options --enable-tests" ;;
debug)
flags="--enable-debug-options --enable-tests" ;;
icc)
flags="CC=icc64 CXX=icpc64 --disable-user-flags --enable-tests" ;;
win32)
flags="CC=i386-pc-mingw32-gcc CXX=i386-pc-mingw32-g++ \
PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=\"\" \
PATH=\"/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH\" \
--with-windows-gtk-name=gtk2-runtime-2.16.6-2010-05-12-ash --disable-user-flags \
--enable-optimize-options --enable-tests --build=i686-linux --host=i386-pc-mingw32" ;;
win32-debug)
flags="CC=i386-pc-mingw32-gcc CXX=i386-pc-mingw32-g++ \
PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=\"\" \
PATH=\"/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH\" \
--disable-user-flags --enable-debug-options --enable-tests --build=i686-linux --host=i386-pc-mingw32" ;;
win64-debug)
flags="CC=x86_64-pc-mingw32-gcc CXX=x86_64-pc-mingw32-g++
PKG_CONFIG_PATH=/cross/w64/target/lib/pkgconfig \
PKG_CONFIG_LIBDIR=\"\" PATH=\"/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH\" \
--disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux \
--host=x86_64-pc-mingw32" ;;
sun)
flags="CC=suncc CXX=sunCC 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" ;;
portland)
flags="CC=pgcc CXX=pgCC LDFLAGS=\"-s\" CFLAGS=\"-Minform=warn -Bstatic_pgi\" \
CXXFLAGS=\"-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE\" \
--enable-tests" ;;
open64)
flags="CC=/opt/open64/bin/opencc CXX=/opt/open64/bin/openCC CXXFLAGS=\"-Wall\" \
--enable-tests" ;;
esac
echo "Running:"
echo $configure_script $flags;
$configure_script $flags
exit $?
+158 -29
View File
@@ -1,5 +1,5 @@
AC_INIT([GSmartControl], [0.8.0rc4], [], [gsmartcontrol])
AC_INIT([GSmartControl],[0.8.6],[],[gsmartcontrol])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS([config.h])
@@ -27,9 +27,18 @@ AC_PROG_CXX
#AC_LANG([C])
AC_LANG([C++])
# to be able to generate static libs
# Needed for generating static libs
AC_CHECK_TOOL(RANLIB, ranlib)
# Win32 resource file compilation
AC_CHECK_TOOL(WINDRES, windres)
# Win32 external dll stripping (already detected by autoconf)
# AC_CHECK_TOOL(STRIP, strip)
# Defines LN_S, needed for man page installation.
AC_PROG_LN_S
# -------------------------------------------------------------------------------------
@@ -46,6 +55,7 @@ AX_COMPILER_VENDOR
# (also define automake conditionals).
APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL_])
APP_DETECT_OS_ENV([target], [CONFIG_OS_ENV_])
APP_DETECT_OS_KERNEL([build], [CONFIG_HOST_KERNEL_])
# Detect common (warning, debug, optimize) compiler flags and
# export them to C(XX)FLAGS, LDFLAGS.
@@ -54,7 +64,13 @@ APP_DETECT_OS_ENV([target], [CONFIG_OS_ENV_])
APP_COMPILER_OPTIONS([])
# Thread support for known configurations.
APP_GET_MT_FLAGS([])
# 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
# Enable _GNU_SOURCE, etc...
APP_USE_SYSTEM_EXTENSIONS([])
@@ -98,7 +114,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__
@@ -118,7 +134,8 @@ AC_CHECK_FUNCS([strtoll _strtoi64], [break])
# 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:
# Header check is the best way to determine availability.
# This defines HAVE_DECL_<symbol> to 1 or 0.
AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# link check only:
# AC_CHECK_FUNCS([strtof strtold])
@@ -152,23 +169,37 @@ AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"])
# -------------------------------------------------------------------------------------
AC_ARG_ENABLE(abort-if-no-gtkmm, AS_HELP_STRING([--disable-abort-if-no-gtkmm],
[disable abort if no gtkmm is found (default: configure aborts when gtkmm is not found)]),
[app_cv_abort_if_no_gtkmm=${enableval}], [app_cv_abort_if_no_gtkmm=yes])
AC_MSG_NOTICE([Abort if no gtkmm is found: $app_cv_abort_if_no_gtkmm])
# 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"
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], [
AC_DEFINE([ENABLE_GLIB], [1], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [1], [Whether GLibmm is installed])
gtkmm_found="yes"
], [
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
if test "x$gtkmm_found" = "xno"; then
if test "x$app_cv_abort_if_no_gtkmm" = "xno"; then
AC_MSG_WARN([$GTKMM_PKG_ERRORS])
AC_MSG_WARN([The program will be unable to compile fully.])
])
# use this instead of the above one to enable abort-on-not-found behaviour.
# PKG_CHECK_MODULES([GTKMM], [$pkg_modules])
else
AC_MSG_ERROR([$GTKMM_PKG_ERRORS])
fi
fi
# GTK flags must be prepended, or glib_config.h conflict between
# gtk 2.x and 1.2 may occur.
@@ -201,7 +232,7 @@ PCRE_LIBS="";
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 "$pcre_found" = "no"; 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`"
@@ -222,7 +253,101 @@ 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"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Win32 distribution support (NSIS, zip)
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)])],
[with_nsis=${withval}], [with_nsis=auto])
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-bindir], [AS_HELP_STRING([--with-windows-bindir],
[specify location of Windows dlls and executables when making Windows packages (default: /cross/w32/target/bin)])],
[with_windows_bindir=${withval}], [with_windows_bindir=auto])
AC_ARG_WITH([windows-gtk-name], [AS_HELP_STRING([--with-windows-gtk-name],
[specify name of Windows GTK+ runtime (installer and unpacked directory) when making Windows packages. It will be looked in windows-bindir.])],
[with_windows_gtk_name=${withval}], [with_windows_gtk_name=])
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\"";
else
with_nsis="~/\".wine/drive_c/Program Files/NSIS/Unicode/makensis.exe\"";
fi
fi
if test "x$with_nsis_wine" = "xauto"; then
# if not building on windows, use wine
if test "x$app_cv_build_os_kernel" = "xwindows32" || test "x$app_cv_build_os_kernel" = "xwindows64"; then
with_nsis_wine="no";
else
with_nsis_wine="yes";
fi
fi
NSIS_EXEC="$with_nsis";
if test "x$with_nsis_wine" != "xno"; then
NSIS_EXEC="wine $with_nsis";
fi
if test "x$with_windows_bindir" = "xauto" || test "x$with_windows_bindir" = "x"; then
with_windows_bindir="/cross/w32/target/bin"
fi
WINDOWS_BINDIR="$with_windows_bindir";
GTK_INSTALLER_NAME="$with_windows_gtk_name";
AC_SUBST(NSIS_EXEC)
AC_SUBST(WINDOWS_BINDIR)
AC_SUBST(GTK_INSTALLER_NAME)
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# 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.glade \
src/res/gsc_add_device_window.glade \
src/res/gsc_executor_log_window.glade \
src/res/gsc_help_window.glade \
src/res/gsc_info_window.glade \
@@ -309,7 +434,8 @@ _AM_SUBST_NOTMAKE([RES_TARGETS])
ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
-DPACKAGE_SYSCONF_DIR=\"\\\"\$(sysconfdir)\\\"\" \
-DTOP_SRC_DIR=\"\\\"\$(top_srcdir)\\\"\""
-DTOP_SRC_DIR=\"\\\"\$(top_srcdir)\\\"\" \
-DHZ_USE_GLOBAL_MACROS=1"
#CFLAGS="$CFLAGS $ADDITIONAL_FLAGS"
CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
@@ -322,16 +448,16 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
# 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 and pathscale, but doesn't work with sun and pgi.
# -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).
# Note: some gcc-frontend-based compilers may have to be added here
# (e.g. gccfss (gcc frontend / sun backend), etc...).
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"
# 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" = "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
# our flags must be before all the others
@@ -402,12 +528,14 @@ AC_SUBST(AUTODIRS)
# -------------------------------------------------------------------------------------
AC_CONFIG_FILES([ data/gsmartcontrol_gnome.desktop data/gsmartcontrol_kde.desktop \
data/gsmartcontrol_other.desktop \
AC_CONFIG_FILES([ data/gsmartcontrol.desktop \
data/nsis/distribution.txt data/nsis/distribution-nogtk.txt data/nsis/gsmartcontrol.nsi \
debian.dist/changelog \
src/gsc_winres.rc src/gsmartcontrol.exe.manifest \
gsmartcontrol.spec version compilation_flags ])
AC_CONFIG_FILES([data/gsmartcontrol_root.sh], [chmod +x data/gsmartcontrol_root.sh])
AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
# these are all the makefiles to generate
@@ -415,7 +543,8 @@ AC_CONFIG_FILES([data/gsmartcontrol_root.sh], [chmod +x data/gsmartcontrol_root.
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/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile \
debian.dist/Makefile])
AC_OUTPUT
+17
View File
@@ -0,0 +1,17 @@
This directory contains scripts and an example crontab to allow GSmartControl
to read smartctl data from cron-generated files. This allows users to read
somewhat recent smartctl information without having to run GSmartControl as
root.
Put the scripts into /usr/local/bin, run GSmartControl and set smartctl path
to "/usr/local/bin/smartctl_subst.sh" (without quotes). An example crontab
runs cron_gather_smart.sh script every 10 minutes, storing its output to
"/var/run/smart-$devname". The smartctl_subst.sh script prints the contents of
this file instead of running smartctl, thus eliminating the need to run
GSmartControl as root when reading SMART values.
Note: These scripts provide read-only capabilities only, they don't allow
changing SMART settings or running self-tests.
Contributed by Alex Butcher <alex dot butcher 'at' assursys.co.uk>.
+22
View File
@@ -0,0 +1,22 @@
#!/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
############################################################################
device="$1";
if [ "$device" = "" ]; then
echo "Usage: $0 <device>"
exit 1;
fi
dev_base="`basename \"$device\"`"
out_file=/var/run/smart-"$dev_base"
# Change the path to smartctl if necessary.
smartctl -a "$device" 2>&1 > "$out_file"
chmod 644 "$out_file"
@@ -0,0 +1,2 @@
*/10 * * * * root /usr/local/bin/cron-smart.sh /dev/sda
*/10 * * * * root /usr/local/bin/cron-smart.sh /dev/sdb
+27
View File
@@ -0,0 +1,27 @@
#!/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
############################################################################
if [ "$1" = "-V" ]; then
smartctl -V 2>&1
exit
fi
while [ $# -ge 1 ]; do
device="$1"
shift
done
if [ "$device" = "" ]; then
echo "Usage: $0 <device>"
exit 1;
fi
dev_base="`basename \"$device\"`"
out_file=/var/run/smart-"$dev_base"
cat "$out_file"
+4
View File
@@ -0,0 +1,4 @@
appicondir = $(datadir)/icons/hicolor/256x256/apps
appicon_DATA = gsmartcontrol.png
EXTRA_DIST = $(appicon_DATA)
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+27 -10
View File
@@ -1,9 +1,23 @@
SUBDIRS = 16 22 24 32 48 64 128
SUBDIRS = 16 22 24 32 48 64 128 256 nsis
dist_man_MANS = gsmartcontrol.1
install-extra-mans:
cd $(DESTDIR)$(mandir)/man1 && $(LN_S) -f gsmartcontrol.1 gsmartcontrol-root.1
uninstall-extra-mans:
cd $(DESTDIR)$(mandir)/man1 && rm -f gsmartcontrol-root.1
# This command is needed if installing with make install (the cache
# file is ignored for rpms).
gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
install-data-hook: update-icon-cache
uninstall-hook: update-icon-cache
install-data-hook: install-extra-mans update-icon-cache
uninstall-hook: uninstall-extra-mans update-icon-cache
update-icon-cache:
@-if test -z "$(DESTDIR)"; then \
echo "Updating GTK icon cache."; \
@@ -14,7 +28,7 @@ update-icon-cache:
fi
# installs to /usr/share/appname
# Application data, installs to /usr/share/appname
appdatadir = $(pkgdatadir)
appdata_DATA = icon_cddvd.png icon_hdd.png
@@ -22,19 +36,22 @@ appdata_DATA = icon_cddvd.png icon_hdd.png
# pkgdatadir = $(datadir)/gsmartcontrol
# pkgdata_DATA =
# Desktop files
desktopdir = $(datadir)/applications
desktop_DATA = gsmartcontrol_gnome.desktop gsmartcontrol_kde.desktop \
gsmartcontrol_other.desktop
desktop_DATA = gsmartcontrol.desktop
# Same as 48/gsmartcontrol.png
# Application pixmap (fallback icon for desktop files).
# Same as 48/gsmartcontrol.png.
# Also, xpm for debian menu, 32x32.
pixmapsdir = $(datadir)/pixmaps
pixmaps_DATA = 48/gsmartcontrol.png
pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm
EXTRA_DIST = $(appdata_DATA) $(pixmaps_DATA)
# Put everything inside the distribution
EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm
bin_SCRIPTS = gsmartcontrol_root.sh
bin_SCRIPTS = gsmartcontrol-root
CLEANFILES = $(bin_SCRIPTS)
+131
View File
@@ -0,0 +1,131 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 - 2010 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@/bin/gsmartcontrol";
prog_name="gsmartcontrol"
DESKTOP="$1";
# This works for --help too
if [ "$DESKTOP" = "" ]; then
DESKTOP="auto"; # default
elif [ "$DESKTOP" != "auto" ] && [ "$DESKTOP" != "kde" ] && \
[ "$DESKTOP" != "gnome" ] && [ "$DESKTOP" != "other" ]; then
echo "Usage: $0 [<auto|kde|gnome|other> [<${prog_name}_options>] ]";
exit 1;
fi
shift; # remove $1
# Auto-detect current desktop if auto was specified.
if [ "$DESKTOP" = "auto" ]; then
# KDE_SESSION_UID is present on kde3 and kde4.
# Note that it may be empty (but still set)
if [ "${KDE_SESSION_UID+set}" = "set" ]; then
DESKTOP="kde";
# same with gnome
elif [ "${GNOME_DESKTOP_SESSION_ID+set}" = "set" ]; then
DESKTOP="gnome";
else
DESKTOP="other";
fi
fi
# echo $DESKTOP;
# They're basically the same, only the order is different.
# 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="su-to-root gnomesu gksu kdesu beesu xdg-su sux";
kde_sus="su-to-root kdesu gnomesu gksu beesu xdg-su sux";
other_sus="$gnome_sus";
candidates="";
found_su=""
if [ "$DESKTOP" = "gnome" ]; then
candidates="$gnome_sus";
elif [ "$DESKTOP" = "kde" ]; then
candidates="$kde_sus";
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 kdesu, gnomesu, gksu, beesu or sux first.";
exit 1;
fi
fi
# gnomesu and gksu (but not kdesu, not sure about others) fail to adopt
# root's PATH. Since the user's PATH may not contain /usr/sbin (with smartctl)
# on some distributions (e.g. mandriva), add it manually. We also add
# /usr/local/sbin, since that's the default location of custom-compiled smartctl.
# 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).
# 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";
export PATH="$EXTRA_PATHS:$PATH"
# echo $found_su;
# 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 $@'";
elif [ "$found_su" = "sux" ]; then
full_cmd="xterm -e sux -c '$EXEC_BIN $@'";
elif [ "$found_su" = "gksu" ]; then
full_cmd="$found_su '$EXEC_BIN $@'";
elif [ "$found_su" = "beesu" ]; then
full_cmd="$found_su -P '$EXEC_BIN $@'";
elif [ "$found_su" = "su-to-root" ]; then
full_cmd="$found_su -X -c '$EXEC_BIN $@'";
else # gnomesu, kdesu, xdg-su
full_cmd="$found_su -c '$EXEC_BIN $@'";
fi
#echo $full_cmd
eval $full_cmd
+74
View File
@@ -0,0 +1,74 @@
.TH GSmartControl "1" "" "gsmartcontrol " "User Commands"
.SH NAME
GSmartControl \- Hard disk drive health inspection tool
.SH SYNOPSIS
\fBgsmartcontrol\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
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.
.PP
\fBgsmartcontrol\-root\fP command launches \fBgsmartcontrol\fP with
administrative privileges. The \fIdesktop\fP argument specifies which desktop
is currently running, for automatic selection of native su mechanism. Valid
values for \fIdesktop\fP are \fBauto\fP, \fBkde\fP, \fBgnome\fP, \fBother\fP.
.SH OPTIONS
.SS "Help Options:"
.TP
\fB\-?\fP, \fB\-\-help\fP
Show help options
.TP
\fB\-\-help\-all\fP
Show all help options
.TP
\fB\-\-help\-gtk\fP
Show GTK+ options
.TP
\fB\-\-help\-debug\fR
Show logging options
.SS "Application Options:"
.TP
\fB\-l\fP, \fB\-\-no\-locale\fP
Disable locale
.TP
\fB\-V\fP, \fB\-\-version\fP
Display version information
.TP
\fB\-\-no\-scan\fP
Don't scan devices on startup
.TP
\fB\-\-no\-hide\-tabs\fP
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
.TP
\fB\-\-add\-device\fP
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
.TP
\fB\-v\fP, \fB\-\-verbose\fP
Enable verbose logging; same as \fB\-\-verbosity\-level\fP 5
.TP
\fB\-q\fP, \fB\-\-quiet\fP
Disable logging; same as \fB\-\-verbosity\-level\fP 0
.TP
\fB\-b\fP, \fB\-\-verbosity\-level\fP
Set verbosity level [0\-5]
.PP
.SH COPYRIGHT
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.
@@ -1,8 +1,5 @@
[Desktop Entry]
# Desktop file for kde only.
OnlyShowIn=KDE;
# desktop file version
Version=1.0
Type=Application
@@ -25,7 +22,8 @@ Icon=gsmartcontrol
#X-KDE-SubstituteUID=true
#X-KDE-RootOnly=true
Exec="@prefix@/bin/gsmartcontrol_root.sh" kde
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol-root"
# Build service rpmlint gives errors without this
X-SuSE-translate=false
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

+394
View File
@@ -0,0 +1,394 @@
/* XPM */
static char * gsmartcontrol_xpm[] = {
"32 32 359 2",
" c None",
". c #ABAAAA",
"+ c #CCCCCC",
"@ c #C5C4C4",
"# c #C2C1C1",
"$ c #D2D1D1",
"% c #C8C7C7",
"& c #C8C7C8",
"* c #C9C8C8",
"= c #CAC8C9",
"- c #CAC9C9",
"; c #C8C6C7",
"> c #C7C6C6",
", c #CFCDCE",
"' c #D1D0D0",
") c #C6C5C6",
"! c #CFCECE",
"~ c #B9B7B7",
"{ c #D4D4D4",
"] c #DCDBDC",
"^ c #D8D7D7",
"/ c #D6D5D5",
"( c #D6D5D6",
"_ c #D7D6D6",
": c #D9D8D8",
"< c #DBDADB",
"[ c #DAD9DA",
"} c #D7D7D7",
"| c #D6D6D6",
"1 c #DAD9D9",
"2 c #E2E1E1",
"3 c #B0AFAF",
"4 c #D3D2D2",
"5 c #CDCCCC",
"6 c #CDCDCD",
"7 c #CECDCD",
"8 c #CECECE",
"9 c #CFCFCF",
"0 c #D0CFCF",
"a c #C0BFBF",
"b c #9C9A9B",
"c c #6E6B6B",
"d c #807E7F",
"e c #C8C8C8",
"f c #CAC9CA",
"g c #CACACA",
"h c #CBCACA",
"i c #CCCBCB",
"j c #CCCBCC",
"k c #D0CECF",
"l c #C3C2C2",
"m c #A09E9F",
"n c #B5B4B4",
"o c #AFADAE",
"p c #787576",
"q c #8D8B8B",
"r c #D4D3D3",
"s c #C7C6C7",
"t c #C7C7C7",
"u c #C9C8C9",
"v c #C9C9C9",
"w c #CACAC9",
"x c #BFBEBE",
"y c #A3A1A2",
"z c #A09D9E",
"A c #B4B2B2",
"B c #B6B4B4",
"C c #827F80",
"D c #969595",
"E c #C4C3C3",
"F c #C4C4C4",
"G c #C5C5C5",
"H c #C6C5C5",
"I c #C6C6C6",
"J c #C9C7C8",
"K c #B9B7B8",
"L c #A5A4A4",
"M c #A4A3A3",
"N c #A7A5A5",
"O c #B3B1B2",
"P c #BFBDBD",
"Q c #8D8A8B",
"R c #A09F9F",
"S c #DDDCDC",
"T c #C0C0C0",
"U c #C3C1C2",
"V c #C3C2C3",
"W c #C3C3C3",
"X c #C4C3C4",
"Y c #B2B1B1",
"Z c #A8A7A7",
"` c #ABA9A9",
" . c #CFCECF",
".. c #C2C0C1",
"+. c #989697",
"@. c #A8A6A7",
"#. c #DEDDDD",
"$. c #DEDDDE",
"%. c #C1C0C0",
"&. c #C2C1C2",
"*. c #B1B0B0",
"=. c #ADACAC",
"-. c #AFAEAE",
";. c #BDBCBC",
">. c #B3B2B2",
",. c #B4B3B4",
"'. c #B8B8B8",
"). c #D5D4D4",
"!. c #C0BFBE",
"~. c #C1C0C1",
"{. c #C3C1C1",
"]. c #C4C4C3",
"^. c #B2B0B1",
"/. c #B3B2B3",
"(. c #B4B3B3",
"_. c #A1A0A0",
":. c #B9B8B8",
"<. c #C4C2C3",
"[. c #BDBBBC",
"}. c #B5B4B3",
"|. c #B6B4B5",
"1. c #B7B5B6",
"2. c #B7B6B7",
"3. c #B8B7B7",
"4. c #B8B7B8",
"5. c #BCBABB",
"6. c #BBBABA",
"7. c #BFBEBF",
"8. c #C1C1C1",
"9. c #C1BFC0",
"0. c #BCBBBB",
"a. c #BEBCBD",
"b. c #BEBDBD",
"c. c #9B999A",
"d. c #9D9B9B",
"e. c #C2C2C2",
"f. c #D4D3D4",
"g. c #CECDCE",
"h. c #C7C5C5",
"i. c #A4A2A3",
"j. c #A19F9F",
"k. c #C6C4C5",
"l. c #DCDCDC",
"m. c #D0CFD0",
"n. c #CECCCD",
"o. c #A2A1A1",
"p. c #939292",
"q. c #E1E0E0",
"r. c #E3E3E3",
"s. c #B3B3B3",
"t. c #CCCACB",
"u. c #D2D2D2",
"v. c #D5D3D4",
"w. c #A7A6A6",
"x. c #959494",
"y. c #E3E2E2",
"z. c #DCDBDB",
"A. c #CBC9CA",
"B. c #E1E0E1",
"C. c #DFDEDE",
"D. c #CECCCC",
"E. c #CDCCCD",
"F. c #D2D0D0",
"G. c #D2D0D1",
"H. c #B5B4B5",
"I. c #B1AFB0",
"J. c #EDECEC",
"K. c #D1CFCF",
"L. c #CDCBCC",
"M. c #D5D5D5",
"N. c #DCDADA",
"O. c #C5C4C5",
"P. c #D2D1D2",
"Q. c #D5D4D5",
"R. c #DADADA",
"S. c #DBDADA",
"T. c #DDDCDD",
"U. c #D7D6D7",
"V. c #E8E7E7",
"W. c #D8D8D8",
"X. c #D8D6D6",
"Y. c #D9D8D9",
"Z. c #DBDBDB",
"`. c #DDDDDD",
" + c #474747",
".+ c #ECEBEB",
"++ c #F3F3F3",
"@+ c #E4E3E3",
"#+ c #E0DFDF",
"$+ c #E5E5E5",
"%+ c #EFEFEF",
"&+ c #EBEAEB",
"*+ c #E6E5E5",
"=+ c #E0E0E0",
"-+ c #E4E4E4",
";+ c #E5E4E4",
">+ c #ECECEC",
",+ c #666666",
"'+ c #585757",
")+ c #D9D9DA",
"!+ c #E2E2E2",
"~+ c #E6E5E6",
"{+ c #E1E1E1",
"]+ c #E6E6E6",
"^+ c #E7E7E7",
"/+ c #898989",
"(+ c #767575",
"_+ c #E0DFE0",
":+ c #E9E9E9",
"<+ c #BDBDBD",
"[+ c #B6B6B6",
"}+ c #B0B0B0",
"|+ c #ACACAC",
"1+ c #ACABAC",
"2+ c #A7A7A7",
"3+ c #DFDFDF",
"4+ c #E5E5E4",
"5+ c #E9E8E8",
"6+ c #E8E8E8",
"7+ c #E3E2E3",
"8+ c #D7D5D5",
"9+ c #8E8D8D",
"0+ c #807F7F",
"a+ c #494848",
"b+ c #5A5758",
"c+ c #605E5F",
"d+ c #646262",
"e+ c #555354",
"f+ c #1F1F1F",
"g+ c #282626",
"h+ c #323031",
"i+ c #3D3C3C",
"j+ c #494748",
"k+ c #555353",
"l+ c #5A5959",
"m+ c #3E3D3D",
"n+ c #434242",
"o+ c #444343",
"p+ c #414041",
"q+ c #3E3E3E",
"r+ c #4A4949",
"s+ c #706D6E",
"t+ c #6B6969",
"u+ c #6A6869",
"v+ c #696469",
"w+ c #656065",
"x+ c #595757",
"y+ c #A5A5A5",
"z+ c #767676",
"A+ c #8A8888",
"B+ c #4B4849",
"C+ c #5C5A5A",
"D+ c #686565",
"E+ c #524F50",
"F+ c #000000",
"G+ c #090607",
"H+ c #171415",
"I+ c #262324",
"J+ c #363334",
"K+ c #454343",
"L+ c #4A4748",
"M+ c #212020",
"N+ c #2A292A",
"O+ c #2C2B2B",
"P+ c #272626",
"Q+ c #242424",
"R+ c #242323",
"S+ c #3C3B3B",
"T+ c #726F70",
"U+ c #6C696A",
"V+ c #6B676A",
"W+ c #7A916C",
"X+ c #7B926B",
"Y+ c #605F60",
"Z+ c #9C9B9C",
"`+ c #606060",
" @ c #8F8D8D",
".@ c #494747",
"+@ c #595657",
"@@ c #615E5F",
"#@ c #656162",
"$@ c #525050",
"%@ c #020101",
"&@ c #0C0A0A",
"*@ c #1A1818",
"=@ c #282525",
"-@ c #454243",
";@ c #494647",
">@ c #2A2929",
",@ c #393838",
"'@ c #363536",
")@ c #323131",
"!@ c #2B292A",
"~@ c #282727",
"{@ c #444243",
"]@ c #696666",
"^@ c #666268",
"/@ c #709D48",
"(@ c #7BAD4A",
"_@ c #68686A",
":@ c #8F8E8F",
"<@ c #4E4D4E",
"[@ c #8E8C8D",
"}@ c #484546",
"|@ c #555253",
"1@ c #5D595A",
"2@ c #5F5B5C",
"3@ c #514F50",
"4@ c #0F0D0D",
"5@ c #211E1F",
"6@ c #312E2F",
"7@ c #413D3E",
"8@ c #222121",
"9@ c #2E2D2D",
"0@ c #312F30",
"a@ c #262425",
"b@ c #464344",
"c@ c #696667",
"d@ c #666263",
"e@ c #5F5E5F",
"f@ c #5A5B5B",
"g@ c #797577",
"h@ c #7A7A7A",
"i@ c #4C4A4A",
"j@ c #2A2728",
"k@ c #2C292A",
"l@ c #302D2E",
"m@ c #312F2F",
"n@ c #2E2C2C",
"o@ c #100F0F",
"p@ c #110F10",
"q@ c #141212",
"r@ c #1B1919",
"s@ c #242222",
"t@ c #2D2B2B",
"u@ c #322F30",
"v@ c #2F2D2D",
"w@ c #2B2829",
"x@ c #292728",
"y@ c #363233",
"z@ c #332F30",
"A@ c #312D2F",
"B@ c #2A2627",
"C@ c #424040",
"D@ c #454444",
"E@ c #737373",
"F@ c #828282",
"G@ c #939293",
"H@ c #9C9C9C",
"I@ c #A3A3A3",
"J@ c #A9A9A9",
"K@ c #ADADAD",
"L@ c #B2B2B2",
"M@ c #B5B5B5",
"N@ c #A4A4A4",
"O@ c #959595",
"P@ c #8C8C8C",
"Q@ c #707070",
"R@ c #FFFFFF",
" ",
" . + @ # $ % & * = - ; > * - - - * , ' ) ! ~ ",
" = { ' ] ^ { / / ( _ : < [ ^ ^ } ^ | 1 2 } 3 ",
" - 4 ' ' 5 6 6 6 7 7 8 9 ! ! 9 9 0 0 $ 5 a b c ",
" d 4 [ * e - - f g h h h i i j + 5 5 k l m n o p ",
" q _ r @ > > s s t % % * * u v - w 5 x y z A B C ",
" D 1 7 l E F @ @ G H H I > > > J * K L M N O P Q ",
" R S % T U U l V W E X F @ @ > E Y Z ` > .* ..+. ",
" @.#.$.W %.# # # &.l l V E G T *.=.-.*.5 ;.>.,.Z ",
" n '.).[ !.T ~.# ..{.U l ].x Y ^.>.A /.(.x ;.O _. ",
" :.^.<.h a %.%.# # # &.U [.}.|.1.2.3.4.K K a 5.N ",
" 6.i H 7.%.8.# &.# l * % E 9.[.5.0.[.;.a.b.;., %.c. ",
" d.> 0 a # &.e.V V % ^ f.j g - % %.....&.l V l g.h.i. ",
" j./ i # E E @ k.H l.g.%.# l E H <.b.; > % % % m.n.o. ",
" p.q.7 @ ) > % % - r.) > % * * g f s.5 t.i i i u.v.w. ",
" x.y.z.> u - g A.- B.C.D.E.! 0 7 (.6.$ ' ' F.G.).^ H. ",
" I.7 J.K.L.7 7 ! ! m.S 1 m.- %.3.8./ ).M./ | _ : N.; ",
" O., F.f.P.4 4 4 f.f.r / Q.4 4 ^ R.: : 1 [ S.< S T.U. ",
" 4 V.| ).| _ W._ X.^ : Y.1 S.Z.z.] S S `.#.C.C.2 V.q. + ",
" S..+++@+`.#+$+%+&+*+2 #+=+q.y.-+-+-+;+$+$+*+>+++Z.*+,+ ",
" '+S.# $ >+r.m.e.# 6 )+!+~+*+@+B.=+{+-+]+]+^+]+^+j 5 y./+ ",
" (+b.3 _+! m.#+:+q.r > <+[+}+|+3 '.b.8.> e 5 7 ! ^+j 1+2+ ",
" x.] ! r / ^ W.R.S 3+2 y.@+4+5+6+^+~+*+;+7+#+C.#.S.8+: V ",
" 9+0+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+n+p+q+r+s+t+u+v+w+x+y+ ",
" z+A+B+C+d+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+ ",
" `+ @.@+@@@#@$@%@&@*@=@J+-@;@>@,@'@)@!@~@{@c ]@^@/@(@_@:@ ",
" <@[@}@|@1@2@3@F+%@4@5@6@7@-@8@9@9@0@~@a@b@c@#@d@e@f@g@h@ ",
" i@j@k@l@m@n@o@p@q@r@s@t@u@!@l@v@v@w@x@l@y@z@6@A@B@C@D@ ",
" E@F@G@H@I@J@K@}+L@M@M@M@s.}+|+N@H@O@P@F@Q@ ",
" R@R@R@R@R@R@R@R@R@ ",
" ",
" "};
-27
View File
@@ -1,27 +0,0 @@
[Desktop Entry]
# Desktop file for gnome only.
OnlyShowIn=GNOME;
# desktop file version
Version=1.0
Type=Application
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
GenericName=Hard Disk Health Inspection
# tooltip
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.
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol_root.sh" gnome
# Build service rpmlint gives errors without this
X-SuSE-translate=false
-27
View File
@@ -1,27 +0,0 @@
[Desktop Entry]
# Desktop file for non-gnome and non-kde only.
NotShowIn=GNOME;KDE;
# desktop file version
Version=1.0
Type=Application
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
GenericName=Hard Disk Health Inspection
# tooltip
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.
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol_root.sh" other
# Build service rpmlint gives errors without this
X-SuSE-translate=false
-82
View File
@@ -1,82 +0,0 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Run gsmartcontrol with root, asking for root password first.
EXEC_BIN="@prefix@/bin/gsmartcontrol";
prog_name="gsmartcontrol"
DESKTOP="$1";
# This works for --help too
if [ "$DESKTOP" = "" ]; then
DESKTOP="other"; # default
elif [ "$DESKTOP" != "kde" ] && [ "$DESKTOP" != "gnome" ] && [ "$DESKTOP" != "other" ]; then
echo "Usage: $0 <desktop_name> [<${prog_name}_options>]";
echo "Desktop name may be kde, gnome, other.";
exit 1;
fi
shift; # remove $1
# They're basically the same, only the order is different.
# sux requires xterm to ask for the password.
# xdg-su is basically like this script, except worse :)
gnome_sus="gnomesu gksu kdesu xdg-su sux";
kde_sus="kdesu gnomesu gksu xdg-su sux";
other_sus="$gnome_sus";
candidates="";
found_su=""
if [ "$DESKTOP" = "gnome" ]; then
candidates="$gnome_sus";
elif [ "$DESKTOP" = "kde" ]; then
candidates="$kde_sus";
elif [ "$DESKTOP" = "other" ]; then
candidates="$other_sus";
fi
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 gnomesu, kdesu or sux first.";
exit 1;
fi
# echo $found_su;
# gnomesu -c 'gsmartcontrol --no-scan'
# kdesu -c 'gsmartcontrol --no-scan'
# xterm -e sux -c 'gsmartcontrol --no-scan' # sux asks for password in a terminal
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 $@'"
else # gnomesu, kdesu, xdg-su
full_cmd="$found_su -c '$EXEC_BIN $@'"
fi
eval $full_cmd
+2
View File
@@ -0,0 +1,2 @@
EXTRA_DIST = distribution.txt.in gsmartcontrol.nsi.in nsi_install.ico nsi_uninstall.ico
+29
View File
@@ -0,0 +1,29 @@
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
http://gsmartcontrol.berlios.de
and
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.16.6-2010-02-21
of gtk2-runtime).
This distribution includes:
* 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. The binary was statically compiled against Gtkmm
(http://www.gtkmm.org/) and related libraries, and PCRE (http://www.pcre.org/).
* Smartctl binaries (from smartmontools 5.41, 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.
See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details.
+28
View File
@@ -0,0 +1,28 @@
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
http://gsmartcontrol.berlios.de
and
http://developer.berlios.de/project/showfiles.php?group_id=10075
This distribution includes:
* 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. The binary was statically compiled against Gtkmm
(http://www.gtkmm.org/) and related libraries, and PCRE (http://www.pcre.org/).
* Smartctl binaries (from smartmontools 5.41, http://smartmontools.sf.net/)
- smartctl-nc.exe (compiled without console output), smartctl.exe (compiled
with console output).
* GTK+ for Windows Runtime package (@GTK_INSTALLER_NAME@)
from http://gtk-win.sf.net, either in original or unpacked form.
The combined distribution is multi-licensed under GNU GPL versions 2 and 3.
See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details.
+519
View File
@@ -0,0 +1,519 @@
; NSIS2 Script
; by Alexander Shaduri <ashaduri 'at' gmail.com>.
; Compatible with NSIS Unicode 2.45.
; Public Domain
; pass /DNO_GTK to makensis to disable inclusion of gtk.
; gtk installer name for embedding
!define GTK_INSTALLER_EXE "@GTK_INSTALLER_NAME@.exe"
!define PRODUCT_VERSION "@VERSION@"
!define PRODUCT_NAME "GSmartControl"
!define PRODUCT_NAME_SMALL "gsmartcontrol"
!define PRODUCT_PUBLISHER "Alexander Shaduri"
!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
; Do a CRC check before installing
CRCCheck On
; This is used in titles
Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION}
; Output File Name
!ifdef NO_GTK
OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-nogtk.exe"
!else
OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}.exe"
!endif
; The Default Installation Directory:
; Try to install to the same directory as runtime.
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
; If already installed, try here
InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory"
ShowInstDetails show
ShowUnInstDetails show
; --------------------- MUI INTERFACE
; MUI 2.0 compatible install
!include "MUI2.nsh"
!include "InstallOptions.nsh" ; section description macros
; Backgound Colors. uncomment to enable fullscreen.
; BGGradient 0000FF 000000 FFFFFF
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "nsi_install.ico"
!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
;!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"
!ifdef NO_GTK
!define LICENSE_FILE "doc\distribution-nogtk.txt"
!else
!define LICENSE_FILE "doc\distribution.txt"
!endif
; Pages to show during installation
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}"
!ifndef NO_GTK
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE on_components_page_leave
!insertmacro MUI_PAGE_COMPONENTS
!endif
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
;!define MUI_FINISHPAGE_RUN "$INSTDIR\gsmartcontrol.exe"
;!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\Example.file"
;!define MUI_FINISHPAGE_RUN_NOTCHECKED
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!insertmacro MUI_PAGE_FINISH
; Uninstaller page
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "English"
; --------------- END MUI
LangString TEXT_IO_TITLE ${LANG_ENGLISH} "GSmartControl"
var install_option_removeold ; uninstall the old version first (if present): yes (default), no.
; ----------------- INSTALLATION TYPES
; InstType "Recommended"
; 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 gsmartcontrol.exe
File gsmartcontrol.exe.manifest
File gsmartcontrol.ico
File icon_*.png
; File *.dll
File smartctl-nc.exe
File smartctl.exe
; Include the "doc" directory completely, excluding the file we're not using (e.g. the -nogtk file in gtk installer).
!ifdef NO_GTK
File /r /x distribution.txt doc
!else
File /r /x distribution-nogtk.txt doc
!endif
; 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 health inspection tool"
SectionEnd
!ifndef NO_GTK
; The SecGtkPrivate and SecGtkPublic sections are mutually exclusive.
Section "GTK+ (for this program only)" SecGtkPrivate
SectionIn 1
SetShellVarContext all ; use all user variables as opposed to current user
AddSize 12200 ; ~ size of unpacked gtk
SetOutPath "$INSTDIR"
File "${GTK_INSTALLER_EXE}"
; TODO: in the future, when we have translations for this program,
; make the GTK+ translations installation dependent on their installation status.
ExecWait '"${GTK_INSTALLER_EXE}" /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S /D=$INSTDIR'
Delete "$INSTDIR\${GTK_INSTALLER_EXE}"
SectionEnd
; disabled by default
Section /o "GTK+ (shared installation)" SecGtkPublic
SectionIn 1
SetShellVarContext all ; use all user variables as opposed to current user
AddSize 12200 ; ~ size of unpacked gtk
SetOutPath "$INSTDIR"
File "${GTK_INSTALLER_EXE}"
ExecWait '"${GTK_INSTALLER_EXE}"'
Delete "$INSTDIR\${GTK_INSTALLER_EXE}"
SectionEnd
!endif ; !NO_GTK
!ifndef NO_GTK
var gtk_mode ; "public", "private" or "none"
var gtk_tmp ; temporary variable
!endif
; 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
; ask for gtk only if it's a non-gtk installer.
!ifdef NO_GTK
Call AskForGtk
!endif
!ifndef NO_GTK
StrCpy $gtk_mode "private" ; default
!endif
FunctionEnd
function .onselchange
!ifndef NO_GTK
; Remember which gtk section was selected.
; Deselect the other section.
; If it was private, we check if public is checked and uncheck private.
StrCmp $gtk_mode "private" check_public ; old selection
StrCmp $gtk_mode "public" check_private ; old selection
goto check_exit
check_public:
SectionGetFlags ${SecGtkPublic} $gtk_tmp ; see if it's checked
IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED}
IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit
SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; unselect the other one
IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF}
SectionSetFlags ${SecGtkPrivate} $gtk_tmp
goto check_exit
check_private:
SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; see if it's checked
IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED}
IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit
SectionGetFlags ${SecGtkPublic} $gtk_tmp ; unselect the other one
IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF}
SectionSetFlags ${SecGtkPublic} $gtk_tmp
check_exit:
; store the current mode
StrCpy $gtk_mode "none"
SectionGetFlags ${SecGtkPrivate} $gtk_tmp
IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED}
IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_private mode_end_private
StrCpy $gtk_mode "private"
mode_end_private:
SectionGetFlags ${SecGtkPublic} $gtk_tmp
IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED}
IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_public mode_end_public
StrCpy $gtk_mode "public"
mode_end_public:
; MessageBox MB_ICONINFORMATION|MB_OK "gtk_mode: $gtk_mode" /SD IDOK
!endif ; !NO_GTK
functionend
!ifndef NO_GTK
Function on_components_page_leave
StrCmp $gtk_mode "none" "" noabort
Call AskForGtk
noabort:
FunctionEnd
!endif ; !NO_GTK
; Section descriptions
!ifndef NO_GTK ; this page is shown only when using gtk
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecMain} "GSmartControl - hard disk drive health inspection tool"
!insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPrivate} "GTK+ libraries, needed by GSmartControl. \
This will install a private version of GTK+, usable only by GSmartControl."
!insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPublic} "GTK+ libraries, needed by GSmartControl. \
This will install a system-wide version of GTK+, shareable with other programs."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
!endif
; ------------------ POST INSTALL
var gtk_dll_abs_path
Section -post
SetShellVarContext all ; use all user variables as opposed to current user
; Don't set any paths for this exe if it has a private GTK+ installation.
!ifndef NO_GTK
StrCmp $gtk_mode "private" skip_exe_PATH
!endif
; set a special path for this exe, as GTK may not be in a global path.
ReadRegStr $gtk_dll_abs_path HKLM "SOFTWARE\GTK\2.0" "DllPath"
WriteRegStr HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe" "Path" "$gtk_dll_abs_path"
!ifndef NO_GTK
skip_exe_PATH:
!endif
!ifndef NO_GTK
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode" "$gtk_mode"
!endif
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR"
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "${PRODUCT_PUBLISHER}"
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
; We don't need this, MUI takes care for us
; WriteRegStr HKCU "Software\${PRODUCT_NAME}" "Installer Language" $sUAGE
; write out uninstaller
WriteUninstaller "$INSTDIR\gsmartcontrol_uninst.exe"
; uninstall shortcut
;CreateDirectory "$SMPROGRAMS\GSmartControl"
;CreateShortCut "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" "$INSTDIR\gsmartcontrol_uninst.exe" "" ""
SectionEnd ; post
; ---------------- UNINSTALL
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." /SD IDOK
FunctionEnd
Section Uninstall
SetShellVarContext all ; use all user variables as opposed to current user
SetAutoClose false
!ifndef NO_GTK
ReadRegStr $gtk_mode HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode"
StrCmp $gtk_mode "private" "" skip_gtk_remove
; remove private GTK+, specify the same custom options are during installation
ExecWait "$INSTDIR\gtk2_runtime_uninst.exe /remove_config=yes /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S"
; _?=$INSTDIR
; Delete "$INSTDIR\gtk2_runtime_uninst.exe" ; If using _? flag, it won't get deleted automatically, do it manually.
skip_gtk_remove:
!endif
; add delete commands to delete whatever files/registry keys/etc you installed here.
Delete "$INSTDIR\gsmartcontrol_uninst.exe"
DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}"
DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}"
Delete "$INSTDIR\gsmartcontrol.exe"
Delete "$INSTDIR\gsmartcontrol.exe.manifest"
Delete "$INSTDIR\gsmartcontrol.ico"
Delete "$INSTDIR\icon_*.png"
; Delete "$INSTDIR\*.dll"
Delete "$INSTDIR\smartctl-nc.exe"
Delete "$INSTDIR\smartctl.exe"
RMDir /r "$INSTDIR\doc"
; clean up generated stuff
Delete "$INSTDIR\*stdout.txt"
Delete "$INSTDIR\*stderr.txt"
RMDir "$INSTDIR" ; only if empty
!ifndef NO_GTK
StrCmp $gtk_mode "private" skip_exe_PATH_remove
!endif
DeleteRegKey HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe"
!ifndef NO_GTK
skip_exe_PATH_remove:
!endif
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
; detect GTK installation (any of available versions)
Function AskForGtk
SetShellVarContext all ; use all user variables as opposed to current user
push $R0
ReadRegStr $R0 HKLM "SOFTWARE\GTK\2.0" "DllPath"
StrCmp $R0 "" no_gtk 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." \
/SD IDOK IDOK have_gtk
;Abort ; Abort has different meaning from onpage callbacks, so use Quit
Quit
goto end_gtk_check
have_gtk:
; do nothing
end_gtk_check:
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
Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

+6
View File
@@ -0,0 +1,6 @@
noinst_DATA = changelog.in compat control copyright menu postinst postrm rules
# include the generated file too
EXTRA_DIST=changelog.in changelog compat control copyright menu postinst \
postrm rules
+5
View File
@@ -0,0 +1,5 @@
gsmartcontrol (@VERSION@+nmu1) unstable; urgency=low
* Permanently Initial Release.
-- Alexander Shaduri <ashaduriREMOVETHIS@gmail.com> Sat, 15 Nov 2008 00:12:04 +0400
+1
View File
@@ -0,0 +1 @@
5
+21
View File
@@ -0,0 +1,21 @@
Source: gsmartcontrol
Section: utils
Priority: extra
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-2.4-dev (>= 2.6.0), libglademm-2.4-dev
Standards-Version: 3.7.3
Package: gsmartcontrol
Architecture: any
#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools, menu
Depends: ${shlibs:Depends}, smartmontools, menu
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 drives. It allows you to inspect the drive's SMART data to determine its
health, as well as run various tests on it.
+32
View File
@@ -0,0 +1,32 @@
This package was debianized by Alexander Shaduri <ashaduri 'at' gmail.com> on
Sat, 15 Nov 2008 00:12:04 +0400.
It was downloaded from <http://gsmartcontrol.berlios.de/>
Copyright:
Copyright (C) 2008 - 2010 Alexander Shaduri <ashaduri 'at' gmail.com>
License:
This product is multi-licensed under GNU GPL versions 2 and 3.
You are free to choose which one you use.
This program is free software: you can redistribute it and/or modify it under
the terms of either version 2 or (at your option) version 3 of the GNU General
Public License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details.
You should have received copies of these licenses along with this program.
If not, see <http://www.gnu.org/licenses/>.
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 - 2010 Alexander Shaduri <ashaduri 'at' gmail.com> and
is licensed under the GPLv2 and GPLv3, see above.
+4
View File
@@ -0,0 +1,4 @@
?package(gsmartcontrol):needs="X11" section="Applications/System/Hardware"\
title="GSmartControl" command="/usr/bin/gsmartcontrol-root"\
longtitle="Hard Disk Health Inspection"\
icon="/usr/share/pixmaps/gsmartcontrol.xpm"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if test -x /usr/bin/update-menus; then update-menus; fi
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if test -x /usr/bin/update-menus; then update-menus; fi
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
config.status: configure
dh_testdir
# Add here commands to configure the package.
ifneq "$(wildcard /usr/share/misc/config.sub)" ""
cp -f /usr/share/misc/config.sub config.sub
endif
ifneq "$(wildcard /usr/share/misc/config.guess)" ""
cp -f /usr/share/misc/config.guess config.guess
endif
./configure --host=$(DEB_HOST_GNU_TYPE) \
--build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man \
--infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs"
# Add --enable-optimize-options=auto to configure line
# for optimized build (may not work on older gcc versions).
build: build-stamp
build-stamp: config.status
dh_testdir
# Add here commands to compile the package.
$(MAKE)
#docbook-to-man debian/gsmartcontrol.sgml > gsmartcontrol.1
touch $@
clean:
dh_testdir
dh_testroot
rm -f build-stamp
# Add here commands to clean up after the build process.
-$(MAKE) clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# Add here commands to install the package into debian/gsmartcontrol.
$(MAKE) DESTDIR=$(CURDIR)/debian/gsmartcontrol install-strip
# Build architecture-independent files here.
binary-indep: build install
# We have nothing to do by default.
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installchangelogs ChangeLog
dh_installdocs
# dh_installexamples
# dh_install
dh_installmenu
# dh_installdebconf
# dh_installlogrotate
# dh_installemacsen
# dh_installpam
# dh_installmime
# dh_python
# dh_installinit
# dh_installcron
# dh_installinfo
dh_installman
dh_link
dh_strip
dh_compress
dh_fixperms
# dh_perl
# dh_makeshlibs
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# (C) 2008 - 2010 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
+3
View File
@@ -0,0 +1,3 @@
[Project]
Manager=KDevCustomBuildSystem
Name=gsmartcontrol
+82 -14
View File
@@ -22,7 +22,7 @@
<kdevautoproject>
<general>
<activetarget>src/gsmartcontrol</activetarget>
<useconfiguration>debuggy</useconfiguration>
<useconfiguration>open64</useconfiguration>
</general>
<run>
<mainprogram>/10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy</mainprogram>
@@ -76,7 +76,7 @@
<f77flags/>
</optimized>
<debuggy>
<configargs>--disable-user-flags --enable-debug-options --enable-tests</configargs>
<configargs>--enable-debug-options --enable-tests</configargs>
<builddir>debuggy</builddir>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
@@ -87,8 +87,8 @@
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompilerbinary/>
<cxxcompilerbinary/>
<ccompilerbinary>gcc-4.4</ccompilerbinary>
<cxxcompilerbinary>g++-4.4</cxxcompilerbinary>
<f77compilerbinary/>
<f77flags/>
</debuggy>
@@ -102,8 +102,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>icc</ccompilerbinary>
<cxxcompilerbinary>icpc</cxxcompilerbinary>
<ccompilerbinary>icc64</ccompilerbinary>
<cxxcompilerbinary>icpc64</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -111,7 +111,7 @@
</icc>
<win32>
<envvars/>
<configargs>--disable-user-flags --enable-debug-options --enable-tests --target=i386-mingw32msvc --host=i386-mingw32msvc --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<configargs>--with-windows-gtk-name=gtk2-runtime-2.16.6-2010-02-24-ash --disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH&quot;</configargs>
<builddir>win32</builddir>
<topsourcedir/>
<cppflags/>
@@ -119,8 +119,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>i386-mingw32msvc-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32msvc-g++</cxxcompilerbinary>
<ccompilerbinary>i386-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-pc-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -128,21 +128,21 @@
</win32>
<suncc>
<envvars>
<envvar value="/opt/sun/sunstudio12/bin:$PATH" name="PATH" />
<envvar value="/opt/sun/sunstudio/bin:$PATH" name="PATH" />
</envvars>
<configargs>--enable-tests</configargs>
<configargs>--enable-tests PKG_CONFIG_PATH=&quot;/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig&quot; PKG_CONFIG_LIBDIR=&quot;&quot;</configargs>
<builddir>suncc</builddir>
<topsourcedir/>
<cppflags/>
<ldflags>-s</ldflags>
<ldflags>-R/opt/sun/target64/lib64 -L/opt/sun/target64/lib64</ldflags>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>suncc</ccompilerbinary>
<cxxcompilerbinary>sunCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-errwarn=%all</cflags>
<cxxflags>-DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<cflags>-errwarn=%all -I/opt/sun/target64/include</cflags>
<cxxflags>-I/opt/sun/target64/include</cxxflags>
<f77flags/>
</suncc>
<portland>
@@ -162,6 +162,74 @@
<cxxflags>-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE</cxxflags>
<f77flags/>
</portland>
<win32-debug>
<envvars/>
<configargs>--disable-user-flags --enable-debug-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH&quot;</configargs>
<builddir>win32-debug</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>i386-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-pc-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</win32-debug>
<win64-debug>
<envvars/>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=x86_64-pc-mingw32 PKG_CONFIG_PATH=/cross/w64/target/lib/pkgconfig PKG_CONFIG_LIBDIR=&quot;&quot; PATH=&quot;/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH&quot;</configargs>
<builddir>win64-debug</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>x86_64-pc-mingw32-gcc</ccompilerbinary>
<cxxcompilerbinary>x86_64-pc-mingw32-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</win64-debug>
<default>
<envvars/>
<configargs/>
<builddir/>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary/>
<cxxcompilerbinary/>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</default>
<open64>
<envvars/>
<configargs>--enable-tests</configargs>
<builddir>open64</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>/opt/open64/bin/opencc</ccompilerbinary>
<cxxcompilerbinary>/opt/open64/bin/openCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags>-Wall</cxxflags>
<f77flags/>
</open64>
</configurations>
<make>
<envvars>
+57 -18
View File
@@ -1,15 +1,20 @@
Name: gsmartcontrol
Version: @VERSION@
Version: @VERSION@
Release: 1as
License: GPLv2, GPLv3
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
Url: http://gsmartcontrol.berlios.de
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
Packager: Alexander Shaduri <ashaduri 'at' gmail.com>
Group: Hardware/Other
Source: gsmartcontrol-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Summary: GSmartControl - Hard Disk Health Inspection Tool
%if 0%{?mandriva_version}
Group: System/Kernel and hardware
%else
Group: Hardware/Other
%endif
# Support build service rpmlint. Disabled - the build service
# stopped recognizing the flag for some architectures.
# %if 0%{?opensuse_bs} && 0%{?suse_version} >= 1010
@@ -22,16 +27,24 @@ Summary: GSmartControl - Hard Disk Health Inspection Tool
# (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}
%if 0%{?%suse_version} >= 1030
Requires: smartmontools, pcre, gtkmm2 >= 2.12.0
BuildRequires: gcc-c++ libstdc++-devel pcre-devel, gtkmm2-devel >= 2.12.0
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.12.0
%else
%if 0%{?%suse_version} == 1020
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
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.6.0, libglademm-devel >= 2.4.0
%else
Requires: smartmontools, pcre, gtkmm24 >= 2.6.0, libglademm >= 2.4.0
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm-devel >= 2.4.0
%endif
%endif
%endif
@@ -43,24 +56,31 @@ BuildRequires: gcc-c++ libstdc++-devel pcre-devel, gtkmm2-devel >= 2.6.0, libgla
# 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
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
BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm24-devel >= 2.4.0
%endif
# Fedora doesn't include gnomesu or anything similar except beesu (since 11), so force it.
# usermode-gtk is for consolehelper-gtk (optional for beesu, needed for GUI auth).
%if 0%{?fedora_version} >= 11
Requires: beesu, usermode-gtk
%endif
%endif
# Mandriva
# 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, libgtkmm2.4_1 >= 2.12.0
BuildRequires: gcc-c++ gcc-cpp libpcre-devel, libgtkmm2.4-devel >= 2.12.0
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, libgtkmm2.4_1 >= 2.6.0, libglademm2.4_1 >= 2.4.0
BuildRequires: gcc-c++ gcc-cpp libpcre-devel, libgtkmm2.4-devel >= 2.6.0, libglademm2.4_1-devel >= 2.4.0
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
@@ -76,13 +96,14 @@ SMART data to determine its health, as well as run various tests on it.
%setup -q
%if 0%{?mandriva_version} <= 2006
%configure
# Don't use configure parameters - let rpm set them.
# --prefix=/usr --enable-default-gcc-options --enable-gcc-optimize
%else
%configure --enable-optimize-options=auto
%endif
%build
# Setup for parallel builds
# Set up for parallel builds
numprocs=`egrep -c ^cpu[0-9]+ /proc/stat || :`
if [ "$numprocs" = "0" ]; then
numprocs=1
@@ -94,7 +115,18 @@ make -j$numprocs
%install
# %makeinstall
make DESTDIR=%buildroot install
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%{?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
%clean
@@ -108,6 +140,7 @@ rm -rf %buildroot
# %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
%doc %{_datadir}/doc/gsmartcontrol
%doc %{_mandir}/man1/*
%{_datadir}/gsmartcontrol
# %{_datadir}/gsmartcontrol/*
@@ -116,11 +149,17 @@ rm -rf %buildroot
%{_datadir}/pixmaps/*
%changelog
* Fri Dec 10 2010 Alexander Shaduri <ashaduri 'at' gmail.com>
- Fixes for Fedora 14.
* 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.
+29 -6
View File
@@ -1,16 +1,39 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
if TARGET_OS_KERNEL_FAMILY_WINDOWS
WINRES_OBJ = gsc_winres_win32.o
endif
# use different name for object file to avoid make auto-rules
gsc_winres_win32.o: $(top_srcdir)/data/gsmartcontrol.ico gsc_winres.rc
cp -f $(top_srcdir)/data/gsmartcontrol.ico .
$(WINDRES) gsc_winres.rc gsc_winres_win32.o
mostlyclean-local:
rm -f gsmartcontrol.ico
noinst_DATA = gsc_winres.rc.in gsmartcontrol.exe.manifest.in
# if TARGET_OS_KERNEL_FAMILY_WINDOWS
# WIN_MAIN_SRC = win_main.cpp
# endif
bin_PROGRAMS = gsmartcontrol
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/res/libres.a
$(top_builddir)/src/res/libres.a $(WINRES_OBJ)
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_executor_error_dialog.h \
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_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 = 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
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_help_window.cpp gsc_info_window.cpp \
gsc_init.cpp gsc_main.cpp gsc_main_window.cpp gsc_preferences_window.cpp
+19 -12
View File
@@ -1,21 +1,24 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
noinst_LIBRARIES = libapplib.a
libapplib_a_SOURCES = app_gtkmm_utils.cpp cmdex.cpp cmdex_sync.cpp \
cmdex_sync_gui.cpp gui_utils.cpp selftest.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_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_device.h \
storage_property.h storage_property_colors.h storage_property_descr.h storage_settings.h \
wrapping_label.h
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 \
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 cmdex.h cmdex_sync.h cmdex_sync_gui.h executor_factory.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 tw_cli_executor.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)
storage_detector_test_SOURCES = storage_detector_test.cpp
storage_detector_test_LDADD = libapplib.a \
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
app_ui_res_utils_test_SOURCES = app_ui_res_utils_test.cpp app_ui_res_utils_test_data.cpp
app_ui_res_utils_test_LDADD = libapplib.a \
$(top_builddir)/src/libdebug/libdebug.a
smartctl_executor_test_SOURCES = smartctl_executor_test.cpp
smartctl_executor_test_LDADD = libapplib.a \
@@ -25,6 +28,10 @@ smartctl_parser_test_SOURCES = smartctl_parser_test.cpp
smartctl_parser_test_LDADD = libapplib.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/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
spawn_test_SOURCES = spawn_test.cpp
# Build them first
@@ -34,7 +41,7 @@ SUBDIRS=../libdebug ../pcrecpp
# we don't list them in a separate variable because otherwise kdevelop won't see them.
noinst_PROGRAMS =
if ENABLE_TESTS
noinst_PROGRAMS += smartctl_executor_test smartctl_parser_test \
noinst_PROGRAMS += app_ui_res_utils_test smartctl_executor_test smartctl_parser_test \
storage_detector_test spawn_test
endif
+2 -2
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -25,7 +25,7 @@
// New tooltip API is available since 2.12
#if !APP_GTKMM_CHECK_VERSION(2, 12, 0)
#define APP_GTKMM_OLD_TOOLTIPS
#define APP_GTKMM_OLD_TOOLTIPS 1
#endif
+10 -15
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -10,10 +10,11 @@
#include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS
#ifdef APP_GTKMM_OLD_TOOLTIPS
#if defined APP_GTKMM_OLD_TOOLTIPS && 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"
@@ -53,7 +54,7 @@ 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 !(defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS)
if (use_markup) {
widget.set_tooltip_markup(tooltip_text);
} else {
@@ -67,16 +68,10 @@ void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
if (tooltips) {
if (use_markup) {
// strip markup
gchar* stripped = 0;
if (pango_parse_markup(tooltip_text.c_str(), -1, 0, NULL, &stripped, NULL, NULL) && stripped) {
gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped, "");
Glib::ustring stripped;
if (app_pango_strip_markup(tooltip_text, stripped)) {
gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped.c_str(), "");
}
g_free(stripped);
// alternate method, check if it works (AttrList has bool() since gtkmm 2.10):
// char dummy = 0;
// Glib::ustring stripped;
// if (Pango::AttrList(tooltip_text, 0, stripped, dummy).gobj()) ...
} else {
gtk_tooltips_set_tip(tooltips, widget.gobj(), tooltip_text.c_str(), "");
@@ -112,7 +107,7 @@ void app_gtkmm_combobox_unset_model(Gtk::ComboBox* box)
#ifdef APP_GTKMM_OLD_TOOLTIPS
#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS
namespace {
@@ -165,7 +160,7 @@ void gtkmm_set_treeview_tooltip_column(Gtk::TreeView* treeview,
if (!treeview)
return;
#ifndef APP_GTKMM_OLD_TOOLTIPS
#if !(defined APP_GTKMM_OLD_TOOLTIPS && 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
@@ -182,7 +177,7 @@ void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview,
if (!iconview)
return;
#ifndef APP_GTKMM_OLD_TOOLTIPS
#if !(defined APP_GTKMM_OLD_TOOLTIPS && 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
+1 -2
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -22,7 +22,6 @@
// Note: This works only if the column has custom widget set.
Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column);
+50
View File
@@ -0,0 +1,50 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <pango/pango.h> // pango_parse_markup()
#include "app_pango_utils.h"
// Returns true if markup was successfully stripped
bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped)
{
gchar* gstripped = 0;
bool ok = false;
if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) {
stripped = gstripped;
ok = true;
}
g_free(gstripped);
// alternative method, check if it works (AttrList has bool() cast since gtkmm 2.10):
// char dummy = 0;
// if (Pango::AttrList(str, 0, stripped, dummy).gobj()) {
// return true;
// }
return ok;
}
bool app_pango_strip_markup(const std::string& str, std::string& stripped)
{
gchar* gstripped = 0;
bool ok = false;
if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) {
stripped = gstripped;
ok = true;
}
g_free(gstripped);
return ok;
}
+25
View File
@@ -0,0 +1,25 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef APP_PANGO_UTILS_H
#define APP_PANGO_UTILS_H
#include <string>
#include <glibmm.h> // ustring
// strip markup of a string. returns true on success.
bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped);
bool app_pango_strip_markup(const std::string& str, std::string& stripped);
#endif
+16 -2
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -26,7 +26,13 @@
inline pcrecpp::RE_Options app_pcre_get_options(const char* modifiers)
{
// ANYCRLF means any of crlf, cr, lf. Used in ^ and $.
// This overrides the build-time newline setting of pcre.
#ifdef PCRE_NEWLINE_ANYCRLF
pcrecpp::RE_Options options(PCRE_NEWLINE_ANYCRLF);
#else
pcrecpp::RE_Options options;
#endif
if (modifiers) {
char c = '\0';
@@ -58,6 +64,8 @@ inline pcrecpp::RE_Options app_pcre_get_options(const char* 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] == '/') {
@@ -71,7 +79,7 @@ inline pcrecpp::RE app_pcre_re(const std::string& perl_pattern)
app_pcre_get_options(perl_pattern.substr(endpos + 1).c_str()));
}
return pcrecpp::RE(perl_pattern);
return pcrecpp::RE(perl_pattern, app_pcre_get_options(0));
}
@@ -194,6 +202,12 @@ inline bool app_pcre_replace_once(const pcrecpp::RE& re, const std::string& repl
// 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);
}
+24 -19
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -12,19 +12,21 @@
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
#include <libglademm.h>
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
#include <gtkmm/builder.h>
#endif
#if GLIBMM_EXCEPTIONS_ENABLED
// 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
#include <memory> // std::auto_ptr
#endif
#include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS
#ifdef APP_GTKMM_OLD_TOOLTIPS
#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS
#include <gtk/gtk.h> // gtk_tooltips_*
#endif
@@ -44,7 +46,7 @@
// Or, if you're using compiled-in buffers, it will make them available.
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".glade", UIResDataBase); \
@@ -56,7 +58,7 @@
}
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".ui", UIResDataBase); \
@@ -84,7 +86,7 @@
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
typedef Glib::RefPtr<Gnome::Glade::Xml> app_ui_res_ref_t;
@@ -97,7 +99,7 @@
return false;
}
#if GLIBMM_EXCEPTIONS_ENABLED
#if !defined GLIBMM_EXCEPTIONS_ENABLED || 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),
@@ -124,7 +126,7 @@
}
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
typedef Glib::RefPtr<Gtk::Builder> app_ui_res_ref_t;
@@ -137,7 +139,7 @@
return false;
}
#if GLIBMM_EXCEPTIONS_ENABLED
#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));
@@ -178,6 +180,8 @@
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;
}
@@ -217,9 +221,9 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
typedef hz::InstanceManager<Child, MultiInstance> instance_class;
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
friend class Gnome::Glade::Xml; // allow construction through libglade
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
friend class Gtk::Builder; // allow construction through gtkbuilder
#endif
friend class hz::InstanceManager<Child, MultiInstance>; // allow construction through instance class
@@ -232,9 +236,9 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
std::string error;
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
app_ui_res_ref_t ui;
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
app_ui_res_ref_t ui = Gtk::Builder::create();
#else // none, just avoid compilation errors
app_ui_res_ref_t ui = 0;
@@ -299,9 +303,9 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
Glib::Object* lookup_object(const Glib::ustring& name)
{
#if defined ENABLE_LIBGLADE
#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE
return static_cast<Glib::Object*>(lookup_widget(name)); // all glade objects are widgets, it's an upcast
#elif defined ENABLE_GTKBUILDER
#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER
return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get().
#else // none, just avoid compilation errors
return 0;
@@ -327,6 +331,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
// This is needed by APP_GLADE_ macros
typedef Child self_type;
typedef WidgetType widget_type;
// protected constructor / destructor, use create() / destroy() instead of new / delete.
@@ -337,7 +342,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
: WidgetType(gtkcobj), ref_ui_(ref_ui)
{
// if gtkmm is older than 2.12, use the old tooltips API
#ifdef APP_GTKMM_OLD_TOOLTIPS
#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS
// attach it to the window
this->create_tooltips_data(this);
#endif
@@ -358,7 +363,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
{ }
#ifdef APP_GTKMM_OLD_TOOLTIPS
#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS
// differentiate Window and its children from other widgets through overloading
void create_tooltips_data(Gtk::Window* window)
+99
View File
@@ -0,0 +1,99 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#include <iostream>
#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"
class AppUiResTestWindow;
// class AppUiResTestWindow : public Gtk::Window
// class AppUiResTestWindow : public hz::InstanceManager<AppUiResTestWindow, false>, public Gtk::Window
class AppUiResTestWindow : public AppUIResWidget<AppUiResTestWindow, false>
{
public:
// name of glade/ui file without a .glade/.ui extension and quotes
APP_UI_RES_DATA_INIT(app_ui_res_test_window);
enum action_t {
action_quit
};
// 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::VBox(false, 5));
add(*vbox);
Gtk::Button* button = Gtk::manage(new Gtk::Button("Clicky"));
vbox->pack_start(*button, Gtk::PACK_SHRINK);
// button->signal_clicked().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked));
button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked2), action_quit));
Gtk::Label* label = Gtk::manage(new Gtk::Label("test"));
vbox->pack_start(*label, Gtk::PACK_SHRINK);
this->signal_delete_event().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_delete_event));
show_all();
}
virtual ~AppUiResTestWindow()
{ }
private:
void on_button_clicked()
{
std::cerr << "AppUiResTestWindow::on_button_clicked()\n";
}
void on_button_clicked2(action_t action_type)
{
std::cerr << "AppUiResTestWindow::on_button_clicked2()\n";
}
bool on_delete_event(GdkEventAny* e)
{
Gtk::Main::quit();
return true; // action handled
}
};
int main(int argc, char *argv[])
{
Gtk::Main m(&argc, &argv);
// AppUiResTestWindow* app = new AppUiResTestWindow();
AppUiResTestWindow::create();
m.run();
// delete app;
AppUiResTestWindow::destroy();
return(0);
}
+582
View File
@@ -0,0 +1,582 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
// A simple empty window definition
#if defined ENABLE_LIBGLADE && 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 && ENABLE_GTKBUILDER
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,
0x3F,
0x3E,
0xA,
0x3C,
0x69,
0x6E,
0x74,
0x65,
0x72,
0x66,
0x61,
0x63,
0x65,
0x3E,
0xA,
0x20,
0x20,
0x3C,
0x6F,
0x62,
0x6A,
0x65,
0x63,
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,
0x6F,
0x62,
0x6A,
0x65,
0x63,
0x74,
0x3E,
0xA,
0x3C,
0x2F,
0x69,
0x6E,
0x74,
0x65,
0x72,
0x66,
0x61,
0x63,
0x65,
0x3E,
0xA,
0x0 };
extern const unsigned int app_ui_res_test_window_ui_size = 224;
#endif
+99 -244
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -14,15 +14,17 @@
// #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"
#include "hz/string_num.h" // hz::number_to_string()
#include "hz/env_tools.h" // hz::ScopedEnv
#include "hz/scoped_ptr.h"
#include "cmdex.h"
using hz::Error;
using hz::ErrorLevel;
@@ -36,41 +38,24 @@ extern "C" {
// callbacks
// 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);
}
inline gboolean cmdex_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
return Cmdex::on_channel_io(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)
inline gboolean cmdex_on_channel_io_stderr(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
return Cmdex::on_channel_io(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
inline gboolean cmdex_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
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);
}
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;
@@ -80,7 +65,6 @@ extern "C" {
}
// this is executed in another thread!
inline gboolean cmdex_on_kill_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
@@ -99,13 +83,11 @@ extern "C" {
bool Cmdex::execute()
{
ScopedLock locker(object_mutex_);
if (this->running_ || this->stopped_cleanup_needed(false))
if (this->running_ || this->stopped_cleanup_needed())
return false;
cleanup_members();
clear_errors(false); // don't lock
clear_errors();
str_stdout_.clear();
str_stderr_.clear();
@@ -115,77 +97,98 @@ bool Cmdex::execute()
// Make command vector
int argcp = 0; // number of args
gchar** argvp = 0; // args vector
GError* shell_error = 0;
if (!g_shell_parse_argv(cmd.c_str(), &argcp, &argvp, &shell_error)) {
push_error(Error<void>("gshell", ErrorLevel::error, shell_error->message), false);
g_error_free(shell_error);
hz::scoped_ptr<gchar*> argvp(0, g_strfreev); // args vector
g_strfreev(argvp);
return false;
{
int argcp = 0; // number of args
hz::scoped_ptr<GError> shell_error(0, g_error_free);
if (!g_shell_parse_argv(cmd.c_str(), &argcp, &argvp.get_ref(), &shell_error.get_ref())) {
push_error(Error<void>("gshell", ErrorLevel::error, shell_error->message), false);
return false;
}
}
// Reset the locale - otherwise it may mangle the output.
// Set the locale for a child to Classic - otherwise it may mangle the output.
// TODO: make this controllable.
const gchar* old_lang_gchar = g_getenv("LANG");
bool old_lang_set = (old_lang_gchar);
std::string old_lang = (old_lang_gchar ? old_lang_gchar : "");
if (old_lang_set)
g_setenv("LANG", "C", true);
bool change_lang = true;
#ifdef _WIN32
// LANG is posix-only, so it has no effect on win32.
// Unfortunately, I was unable to find a way to execute a child with a different
// locale in win32. Locale seems to be non-inheritable, so setting it here won't help.
change_lang = false;
#endif
hz::ScopedEnv lang_env("LANG", "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();
while (*elem) {
debug_out_dump("app", *elem << "\n");
++elem;
}
}
*/
// Execute the command
gchar* curr_dir = g_get_current_dir();
GError* spawn_error = 0;
hz::scoped_ptr<gchar> curr_dir(g_get_current_dir(), g_free);
hz::scoped_ptr<GError> spawn_error(0, g_error_free);
if (!g_spawn_async_with_pipes(curr_dir, argvp, NULL,
GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
NULL, NULL, // child setup function
&this->pid_, 0, &fd_stdout_, &fd_stderr_, &spawn_error)) {
push_error(Error<void>("gspawn", ErrorLevel::error, spawn_error->message), false);
g_error_free(spawn_error);
g_free(curr_dir);
g_strfreev(argvp);
if (old_lang_set)
g_setenv("LANG", old_lang.c_str(), true);
return false;
}
g_free(curr_dir);
g_strfreev(argvp);
if (old_lang_set)
g_setenv("LANG", old_lang.c_str(), true);
/*
#if defined APP_CMDEX_USE_SYNC && APP_CMDEX_USE_SYNC
hz::scoped_ptr<gchar*> stdout_str(0, g_free);
hz::scoped_ptr<gchar*> stderr_str(0, g_free);
gint exit_status = 0;
g_timer_start(timer_); // start the timer
if (!g_spawn_sync(curr_dir.get(), argvp.get(), NULL,
GSpawnFlags(G_SPAWN_SEARCH_PATH),
NULL, NULL, // child setup function
&stdout_str.get_ref(), &stderr_str.get_ref(), &exit_status, &spawn_error.get_ref()))
{
// no data is returned to &-parameters on error.
push_error(Error<void>("gspawn", ErrorLevel::error, spawn_error->message), false);
return false;
}
#ifdef _WIN32
channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_);
channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_);
#else
channel_stdout_ = g_io_channel_unix_new(fd_stdout_);
channel_stderr_ = g_io_channel_unix_new(fd_stderr_);
#endif
#else // async way:
*/
if (!g_spawn_async_with_pipes(curr_dir.get(), argvp.get(), NULL,
GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
NULL, NULL, // child setup function
&this->pid_, 0, &fd_stdout_, &fd_stderr_, &spawn_error.get_ref()))
{
// no data is returned to &-parameters on error.
push_error(Error<void>("gspawn", ErrorLevel::error, spawn_error->message), false);
return false;
}
g_timer_start(timer_); // start the timer
// #endif
#ifdef _WIN32
channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_);
channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_);
#else
channel_stdout_ = g_io_channel_unix_new(fd_stdout_);
channel_stderr_ = g_io_channel_unix_new(fd_stderr_);
#endif
// The internal encoding is always UTF8. To read command output correctly, use
// "" for binary data, or set io encoding to current locale.
// 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).
GIOFlags channel_flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
int channel_flags = ~G_IO_FLAG_NONBLOCK;
// Note about GError's here:
// What do we do? The command is already running, so let's ignore these
@@ -196,55 +199,35 @@ 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_, channel_flags, 0);
g_io_channel_set_flags(channel_stdout_, GIOFlags(g_io_channel_get_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_, channel_flags, 0);
g_io_channel_set_flags(channel_stderr_, GIOFlags(g_io_channel_get_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);
gint io_thread_priority = G_PRIORITY_HIGH; // channel reader callback thread must read enough so that the loss is minimal
// Channel reader callback must be called before other stuff so that the loss is minimal.
gint io_priority = G_PRIORITY_HIGH;
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_stdout_ = g_io_add_watch_full(channel_stdout_, io_priority, cond,
&cmdex_on_channel_io_stdout, this, NULL);
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);
}
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_priority, cond,
&cmdex_on_channel_io_stderr, this, NULL);
// 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; // process is running now.
// actually, it may be already stopped, but the watcher function is blocked
// until we exit, so the actions are serialized.
this->running_ = true; // the process is running now.
return true;
}
@@ -255,8 +238,6 @@ bool Cmdex::execute()
// send SIGTERM(15) (terminate)
bool Cmdex::try_stop(hz::signal_t sig)
{
ScopedLock locker(object_mutex_);
if (!this->running_ || this->pid_ <= 0)
return false;
@@ -281,12 +262,11 @@ bool Cmdex::try_stop(hz::signal_t sig)
void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec)
{
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(false);
unset_stop_timeouts();
if (term_timeout_msec != 0)
event_source_id_term = g_timeout_add(term_timeout_msec, &cmdex_on_term_timeout, this);
@@ -298,10 +278,8 @@ void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec)
void Cmdex::unset_stop_timeouts(bool do_lock)
void Cmdex::unset_stop_timeouts()
{
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)
@@ -323,14 +301,12 @@ void Cmdex::unset_stop_timeouts(bool do_lock)
void Cmdex::stopped_cleanup()
{
// DBG_FUNCTION_ENTER_MSG;
ScopedLock locker(object_mutex_); // member access / serialization lock
if (this->running_ || !this->stopped_cleanup_needed(false)) // huh?
if (this->running_ || !this->stopped_cleanup_needed()) // huh?
return;
// remove stop timeout callbacks
unset_stop_timeouts(false);
unset_stop_timeouts();
// various statuses (see waitpid (2)):
if (WIFEXITED(waitpid_status_)) { // exited normally
@@ -368,29 +344,19 @@ void Cmdex::stopped_cleanup()
// NOTE: This function is called in a separate thread!
// Called when child exits
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
// Remove fd IO callbacks. They may actually be removed already.
// Remove fd IO callbacks. They may actually be removed already (note sure about this).
// 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_) {
@@ -410,13 +376,9 @@ 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;
}
@@ -424,13 +386,10 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
// 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,
gboolean Cmdex::on_channel_io(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type)
{
// debug_out_dump("app", "Cmdex::on_channel_io_as_available("
// debug_out_dump("app", "Cmdex::on_channel_io("
// << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n");
bool continue_events = true;
@@ -446,8 +405,6 @@ gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
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) {
@@ -459,12 +416,11 @@ gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
}
// while there's anything to read, read it
do {
GError* channel_error = 0;
hz::scoped_ptr<GError> channel_error(0, g_error_free);
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(channel, buf, count, &bytes_read, &channel_error);
GIOStatus status = g_io_channel_read_chars(channel, buf, count, &bytes_read, &channel_error.get_ref());
if (bytes_read)
output_str->append(buf, bytes_read);
@@ -480,107 +436,6 @@ gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
}
} while (g_io_channel_get_buffer_condition(channel) & G_IO_IN);
// false if the source should be removed, true otherwise.
return continue_events;
}
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)
{
// 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 {
GError* channel_error = 0;
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(source, buf, count, &bytes_read, &channel_error);
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.
+22 -97
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -10,41 +10,34 @@
#include <string>
#include <glib.h>
#include "hz/process_signal.h" // hz::SIGNAL_*, hz::process_signal_send (cpp), win32's W* (cpp)
#include "hz/process_signal.h" // hz::SIGNAL_*
#include "hz/error_holder.h"
#include "hz/sync.h"
#include "hz/sync_policy_glib.h"
#include "hz/sync.h" // 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).
1. Add a callback to signal_exited.
2. Manually poll stopped_cleanup_needed() from the main thread.
2. Manually poll stopped_cleanup_needed() (from the same thread).
In both cases, stopped_cleanup() must be called afterwards,
from the _main_ thread.
In both cases, stopped_cleanup() must be called afterwards
(from the main thread).
*/
// inheriting from sigc::trackable automatically disconnects this class's
// slots upon destruction.
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyNone> {
public:
using hz::ErrorHolder<hz::SyncPolicyGlib>::ptr_error_list_t; // auto-deleting container
using hz::ErrorHolder<hz::SyncPolicyGlib>::error_list_t;
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorScopedLock ScopedLock;
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorLockPolicy LockPolicy;
using hz::ErrorHolder<hz::SyncPolicyNone>::ptr_error_list_t; // auto-deleting container
using hz::ErrorHolder<hz::SyncPolicyNone>::error_list_t;
typedef std::string (*exit_status_translator_func_t)(int, void*);
typedef void (*exited_callback_func_t)(void*);
@@ -54,15 +47,13 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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),
: 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),
stdout_make_str_as_available_(false), stderr_make_str_as_available_(true),
translator_func_(0), translator_func_data_(0),
exited_callback_(exited_cb), exited_callback_data_(exited_cb_data)
{ }
@@ -85,6 +76,9 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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;
@@ -116,9 +110,7 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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);
void unset_stop_timeouts();
// If stopped_cleanup_needed() returned true, call this. The command
@@ -128,10 +120,8 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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)
bool stopped_cleanup_needed()
{
ScopedLock locker(object_mutex_, do_lock);
return (child_watch_handler_called_);
}
@@ -141,7 +131,6 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// stopped_cleanup_needed() instead.
bool is_running() const
{
ScopedLock locker(object_mutex_);
return running_;
}
@@ -149,20 +138,19 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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
// 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 in another thread, the buffer may not get read and
// 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 thread priority (which may not help).
// Another way is to delay the command exit so that the event source thread
// 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 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)
@@ -170,29 +158,12 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
}
// 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.
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_;
@@ -206,7 +177,6 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// 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();
@@ -219,62 +189,28 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
// Return execution time, in seconds. Call after execute().
double get_execution_time()
{
ScopedLock locker(object_mutex_);
gulong microsec = 0;
return g_timer_elapsed(timer_, &microsec);
}
// 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;
}
// 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.
// You can 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
// for passing to callback
@@ -288,20 +224,15 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
static void on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data);
static gboolean on_channel_io_buffered(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type);
static gboolean on_channel_io_as_available(GIOChannel* source,
static gboolean on_channel_io(GIOChannel* source,
GIOCondition cond, Cmdex* self, channel_t type);
private:
// 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;
@@ -324,8 +255,6 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
}
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. NOT affected by cleanup().
@@ -358,9 +287,6 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
int event_source_id_stdout_;
int event_source_id_stderr_;
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().
@@ -372,7 +298,6 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
void* translator_func_data_;
// "command exited" signal callback.
// NOTE: This will be called in separate thread!
exited_callback_func_t exited_callback_; // NOT affected by cleanup().
void* exited_callback_data_;
+4 -2
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -87,8 +87,10 @@ 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);
}
+2 -8
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -179,13 +179,6 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
cmdex_.set_buffer_sizes(stdout_buffer_size, stderr_buffer_size);
}
// 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);
@@ -209,6 +202,7 @@ 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;
+1 -1
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
+1 -1
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
+51
View File
@@ -0,0 +1,51 @@
/**************************************************************************
Copyright:
(C) 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "hz/debug.h"
#include "executor_factory.h"
#include "smartctl_executor_gui.h"
#include "tw_cli_executor.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());
}
}
DBG_ASSERT(0);
return hz::intrusive_ptr<CmdexSync>();
}
+55
View File
@@ -0,0 +1,55 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#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
};
/// 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
};
typedef hz::intrusive_ptr<ExecutorFactory> ExecutorFactoryRefPtr;
#endif
+95 -3
View File
@@ -1,16 +1,21 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#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 {
inline void show_dialog(const std::string& message, const std::string& sec_message,
Gtk::Window* parent, Gtk::MessageType type, bool sec_msg_markup)
@@ -75,5 +80,92 @@ void gui_show_info_dialog(const std::string& message, const std::string& sec_mes
// 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)
{
return gui_show_text_entry_dialog(title, message, "", result, default_str, parent, false);
}
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, bool sec_msg_markup)
{
int response = 0;
std::string input_str;
{ // the dialog hides at the end of scope
Gtk::Dialog dialog(title, true); // modal
dialog.set_resizable(false);
dialog.set_skip_taskbar_hint(true);
dialog.set_border_width(5);
if (parent) {
dialog.set_transient_for(*parent);
dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
} else {
dialog.set_position(Gtk::WIN_POS_MOUSE);
}
Gtk::Label main_label;
main_label.set_markup("<big><b>" + Glib::Markup::escape_text(message)
+ (sec_message.empty() ? "\n" : "") + "</b></big>");
main_label.set_line_wrap(true);
main_label.set_selectable(true);
main_label.set_alignment(0.0, 0.0);
Gtk::Label sec_label;
if (sec_msg_markup) {
sec_label.set_markup(sec_message);
} else {
sec_label.set_text(sec_message);
}
sec_label.set_line_wrap(true);
sec_label.set_selectable(true);
sec_label.set_alignment(0.0, 0.0);
Gtk::Entry input_entry;
input_entry.set_activates_default(true);
Gtk::VBox vbox;
vbox.set_spacing(12);
vbox.pack_start(main_label, false, false, 0);
vbox.pack_start(sec_label, true, true, 0);
vbox.pack_start(input_entry, true, true, 0);
vbox.show_all();
dialog.get_action_area()->set_border_width(5);
dialog.get_action_area()->set_spacing(6);
dialog.get_vbox()->set_spacing(14); // as in MessageDialog
dialog.get_vbox()->pack_start(vbox, false, false, 0);
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
Gtk::Button ok_button(Gtk::Stock::OK);
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
response = dialog.run(); // blocks until the dialog is closed
input_str = input_entry.get_text();
}
if (response == Gtk::RESPONSE_OK) {
result = input_str;
return true;
}
return false;
}
+11 -1
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -14,6 +14,8 @@
// These functions won't return until the dialogs are closed.
// Message must not contain any markup.
void gui_show_error_dialog(const std::string& message, Gtk::Window* parent = 0);
void gui_show_error_dialog(const std::string& message, const std::string& sec_message,
@@ -32,6 +34,14 @@ void gui_show_info_dialog(const std::string& message, const std::string& sec_mes
Gtk::Window* parent = 0, bool sec_msg_markup = false);
// 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);
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);
+10 -8
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -21,7 +21,7 @@ int64_t SelfTest::get_remaining_seconds() const
if (total <= 0)
return -1; // unknown
double gran = (total / 9.); // seconds per 10%
double gran = (double(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();
@@ -103,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_smartctl("-t " + test_param, smartctl_ex, output); // --test=
std::string error_msg = drive_->execute_device_smartctl("-t " + test_param, smartctl_ex, output); // --test=
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -163,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_smartctl("-X", smartctl_ex, output); // --abort
std::string error_msg = drive_->execute_device_smartctl("-X", smartctl_ex, output); // --abort
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -202,8 +202,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
return "Invalid drive given.";
std::string 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
// std::string error_msg = drive_->execute_device_smartctl("-l selftest", smartctl_ex, output); // --log=
std::string error_msg = drive_->execute_device_smartctl("-c", smartctl_ex, output); // --capabilities
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -251,7 +251,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
poll_in_seconds_ = 30; // just a guess
} else {
double gran = (total / 9.); // seconds per 10%. use double, because e.g. 60sec test gives silly values with int.
// seconds per 10%. use double, because e.g. 60sec test gives silly values with int.
double gran = (double(total) / 9.);
// 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.)));
@@ -262,7 +263,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
poll_in_seconds_ = std::max(int64_t(1*60), int64_t(gran / 10.)); // that's 2 min for 180min extended test
debug_out_dump("app", DBG_FUNC_MSG << "total: " << total << ", gran: " << gran
<< ", poll_in: " << poll_in_seconds_ << ", remaining: " << get_remaining_seconds() << ".\n");
<< ", poll in: " << poll_in_seconds_ << ", remaining secs: " << get_remaining_seconds()
<< ", remaining %: " << int(remaining_percent_) << ", last seen %: " << int(last_seen_percent_) << ".\n");
}
} else {
+2 -2
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -8,9 +8,9 @@
#define SELFTEST_H
#include <string>
#include <stdint.h>
#include <glibmm/timer.h>
#include "hz/cstdint.h"
#include "hz/intrusive_ptr.h"
#include "storage_device.h"
+135
View File
@@ -0,0 +1,135 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#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;
rconfig::get_data("system/smartctl_binary", smartctl_binary);
#ifdef _WIN32
// look in smartmontools installation directory.
do {
bool use_smt = false;
rconfig::get_data("system/win32_search_smartctl_in_smartmontools", use_smt);
if (!use_smt)
break;
std::string smt_regpath, smt_regkey, smt_smartctl;
rconfig::get_data("system/win32_smartmontools_regpath", smt_regpath);
rconfig::get_data("system/win32_smartmontools_regkey", smt_regkey);
rconfig::get_data("system/win32_smartmontools_smartctl_binary", smt_smartctl);
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()) {
debug_out_info("app", DBG_FUNC_MSG << "Smartmontools installation not found in \"HKLM\\"
<< smt_regpath << "\\" << smt_regkey << "\".\n");
break;
}
debug_out_info("app", DBG_FUNC_MSG << "Smartmontools installation found at \"" << smt_inst_dir
<< "\", using \"" << smt_smartctl << "\".\n");
hz::FsPath p(smt_inst_dir);
p.append(smt_smartctl);
if (!p.exists() || !p.is_file())
break;
smartctl_binary = p.str();
} while (false);
#endif
return 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 << "Error while executing smartctl binary.\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();
}
+13 -5
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -132,7 +132,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
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;
}
@@ -140,9 +140,6 @@ class SmartctlExecutorGeneric : public ExecutorSync {
this->set_error_msg(e->get_message());
}
};
@@ -150,6 +147,17 @@ class SmartctlExecutorGeneric : public ExecutorSync {
typedef SmartctlExecutorGeneric<CmdexSync> SmartctlExecutor;
typedef hz::intrusive_ptr<SmartctlExecutor> SmartctlExecutorRefPtr;
// returns an empty string if not found.
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);
+1 -1
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
+3 -3
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#include <iostream>
@@ -14,7 +14,7 @@
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),
+320 -144
View File
@@ -1,11 +1,13 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <stdint.h> // uint64_t
#include <clocale> // localeconv
#include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
#include "hz/cstdint.h" // uint64_t
#include "hz/string_algo.h" // string_*
#include "hz/string_num.h" // string_is_numeric, number_to_string
#include "hz/format_unit.h" // format_size
@@ -17,6 +19,7 @@
#include "storage_property_colors.h"
namespace {
@@ -147,23 +150,34 @@ bool SmartctlParser::parse_full(const std::string& full)
// version info
std::string version = parse_version(s);
if (version.empty()) {
std::string version, version_full;
if (!parse_version(s, version, version_full)) {
set_error_msg("Cannot extract smartctl version information.");
debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n");
return false;
} else {
StorageProperty p;
p.set_name("Smartctl version", "smartctl_version");
p.reported_value = version;
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
p.section = StorageProperty::section_info; // add to info section
add_property(p);
{
StorageProperty p;
p.set_name("Smartctl version", "smartctl_version", "Smartctl Version");
p.reported_value = version;
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
p.section = StorageProperty::section_info; // add to info section
add_property(p);
}
{
StorageProperty p;
p.set_name("Smartctl version", "smartctl_version_full", "Smartctl Version");
p.reported_value = version_full;
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
p.section = StorageProperty::section_info; // add to info section
add_property(p);
}
}
if (!check_version(version)) {
if (!check_version(version, version_full)) {
set_error_msg("Incompatible smartctl version.");
debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n");
return false;
@@ -197,35 +211,38 @@ bool SmartctlParser::parse_full(const std::string& full)
section_start_pos = section_end_pos;
}
return status;
if (!status) {
set_error_msg("No ATA sections could be parsed.");
debug_out_warn("app", DBG_FUNC_MSG << "No ATA sections could be parsed. Returning.\n");
return false;
}
return true;
}
// Supply output of "smartctl --version" here.
// returns empty string on failure. Non-unix newlines in s are ok.
std::string SmartctlParser::parse_version(const std::string& s)
// returns false on failure. Non-unix newlines in s are ok.
bool SmartctlParser::parse_version(const std::string& s, std::string& version, std::string& version_full)
{
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// e.g. "smartctl version 5.37" or "smartctl 5.39"
pcrecpp::RE re("^smartctl (?:version )?([0-9][^ \\t\\n\\r]+)", re_options);
std::string version;
if (!re.PartialMatch(s, &version)) {
// "smartctl 5.39 2009-06-03 20:10" (cvs versions)
// "smartctl 5.39 2009-08-08 r2873" (svn versions)
if (!app_pcre_match("/^smartctl (?:version )?(([0-9][^ \\t\\n\\r]+)(?: [0-9 r:-]+)?)/mi", s, &version_full, &version)) {
debug_out_error("app", DBG_FUNC_MSG << "No smartctl version information found in supplied string.\n");
return std::string();
return false;
}
hz::string_trim(version_full);
return version;
return true;
}
// check that the version of smartctl output can be parsed with this parser.
bool SmartctlParser::check_version(const std::string& version_str)
bool SmartctlParser::check_version(const std::string& version_str, const std::string& version_full_str)
{
// tested with 5.1-xx versions (1 - 18), and 5.[20 - 38].
// note: 5.1-11 (maybe others too) with scsi disk gives non-parsable output (why?).
@@ -249,10 +266,40 @@ bool SmartctlParser::check_version(const std::string& version_str)
// Note: this property is present since 5.33.
std::string SmartctlParser::parse_byte_size(const std::string& str, uint64_t& bytes, bool extended)
{
// e.g. "500,107,862,016" bytes or "80'060'424'192 bytes" or "80 026 361 856 bytes"
// (locale-dependent?). added '.'-separated too, just in case.
std::string s = hz::string_replace_copy(hz::string_replace_copy(hz::string_trim_copy(str), ",", ""), "'", "");
s = hz::string_replace_copy(hz::string_replace_copy(hz::string_replace_copy(s, " bytes", ""), " ", ""), ".", "");
// E.g. "500,107,862,016" bytes or "80'060'424'192 bytes" or "80 026 361 856 bytes".
// French locale inserts 0xA0 as a separator (non-breaking space, _not_ a valid utf8 char).
// Added '.'-separated too, just in case.
// Smartctl uses system locale's thousands_sep explicitly.
// When launching smartctl, we use LANG=C for it, but it works only on POSIX.
// Also, loading smartctl output files from different locales doesn't really work.
// debug_out_dump("app", "Size reported as: " << str << "\n");
std::vector<std::string> to_replace;
to_replace.push_back(" ");
to_replace.push_back("'");
to_replace.push_back(",");
to_replace.push_back(".");
to_replace.push_back(std::string(1, 0xa0));
#ifdef _WIN32
// if current locale is C, then probably we didn't change it at application
// startup, so set it now (temporarily). Otherwise, just use the current locale's
// thousands separator.
{
std::string old_locale = hz::locale_c_get();
hz::ScopedCLocale loc("", old_locale == "C"); // set system locale if the current one is C
struct lconv* lc = std::localeconv();
if (lc && lc->thousands_sep && *(lc->thousands_sep)) {
to_replace.push_back(lc->thousands_sep);
}
} // the locale is restored here
#endif
to_replace.push_back("bytes");
std::string s = hz::string_replace_array_copy(hz::string_trim_copy(str), to_replace, "");
uint64_t v = 0;
if (hz::string_is_numeric(s, v, false)) {
@@ -308,16 +355,13 @@ bool SmartctlParser::parse_section(const std::string& header, const std::string&
// Parse the info section (without "===" header)
bool SmartctlParser::parse_section_info(const std::string& body)
{
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
this->set_data_section_info(body);
StorageProperty::section_t section = StorageProperty::section_info;
// split by lines.
// e.g. Device Model: ST3500630AS
pcrecpp::RE re("^([^:\\n]+):[ \\t]*(.*)$", re_options);
pcrecpp::RE re = app_pcre_re("/^([^\\n]+): [ \\t]*(.*)$/miU"); // ungreedy
std::string name, value;
pcrecpp::StringPiece input(body); // position tracker
@@ -357,11 +401,38 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
}
if (app_pcre_match("/Model Family/mi", p.reported_name)
|| app_pcre_match("/Device Model/mi", p.reported_name)
|| app_pcre_match("/Serial Number/mi", p.reported_name)
|| app_pcre_match("/Firmware Version/mi", p.reported_name)
) {
if (app_pcre_match("/Model Family/mi", p.reported_name)) {
p.set_name(p.reported_name, "model_family", "Model Family");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/Device Model/mi", p.reported_name)) {
p.set_name(p.reported_name, "device_model", "Device Model");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/Serial Number/mi", p.reported_name)) {
p.set_name(p.reported_name, "serial_number", "Serial Number");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/Firmware Version/mi", p.reported_name)) {
p.set_name(p.reported_name, "firmware_version", "Firmware Version");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/Sector Sizes/mi", p.reported_name)) {
p.set_name(p.reported_name, "sector_sizes", "Sector Sizes");
p.value_type = StorageProperty::value_type_string; // prints 2 values (phys/logical, if they're different)
p.value_string = p.reported_value;
} else if (app_pcre_match("/Sector Size/mi", p.reported_name)) {
p.set_name(p.reported_name, "sector_size", "Sector Size");
p.value_type = StorageProperty::value_type_string; // prints a single value (if it's not 512)
p.value_string = p.reported_value;
} else if (app_pcre_match("/LU WWN Device Id/mi", p.reported_name)) {
p.set_name(p.reported_name, "wwn_id", "World Wide Name");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
@@ -376,6 +447,7 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
p.value_string = p.reported_value;
} else if (app_pcre_match("/User Capacity/mi", p.reported_name)) {
p.set_name(p.reported_name, "capacity", "Capacity");
p.value_type = StorageProperty::value_type_integer;
uint64_t v = 0;
if ((p.readable_value = parse_byte_size(p.reported_value, v, true)).empty()) {
@@ -392,7 +464,7 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
p.value_integer = v;
} else if (app_pcre_match("/Device is/mi", p.reported_name)) {
p.set_name(p.reported_name, "in_smartctl_db", "Is in smartctl database");
p.set_name(p.reported_name, "in_smartctl_db", "In Smartctl Database");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = (!app_pcre_match("/Not in /mi", p.reported_value));
@@ -401,37 +473,40 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/Available - device has/mi", p.reported_value)) {
p.set_name(p.reported_name, "smart_supported", "SMART supported");
p.set_name(p.reported_name, "smart_supported", "SMART Supported");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = true;
} else if (app_pcre_match("/Enabled/mi", p.reported_value)) {
p.set_name(p.reported_name, "smart_enabled", "SMART enabled");
p.set_name(p.reported_name, "smart_enabled", "SMART Enabled");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = true;
} else if (app_pcre_match("/Disabled/mi", p.reported_value)) {
p.set_name(p.reported_name, "smart_enabled", "SMART enabled");
p.set_name(p.reported_name, "smart_enabled", "SMART Enabled");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = false;
} else if (app_pcre_match("/Unavailable/mi", p.reported_value)) {
p.set_name(p.reported_name, "smart_supported", "SMART supported");
p.set_name(p.reported_name, "smart_supported", "SMART Supported");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = false;
// this should be last - when ambiguous state is detected, usually smartctl
// retries with other methods and prints one of the above.
} else if (app_pcre_match("/Ambiguous/mi", p.reported_value)) {
p.set_name(p.reported_name, "smart_supported", "SMART supported");
p.set_name(p.reported_name, "smart_supported", "SMART Supported");
p.value_type = StorageProperty::value_type_bool;
p.value_bool = true; // let's be optimistic - just hope that it doesn't hurt.
}
} else {
debug_out_warn("app", DBG_FUNC_MSG << "Unknown attribute \"" << p.reported_name << "\"\n");
debug_out_warn("app", DBG_FUNC_MSG << "Unknown property \"" << p.reported_name << "\"\n");
// this is not an error, just unknown attribute. treat it as string.
// Don't highlight it with warning, it may just be a new smartctl feature.
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
}
return true;
@@ -464,7 +539,7 @@ bool SmartctlParser::parse_section_data(const std::string& body)
// merge "error log" parts. each part begins with a double-space or "Error nn".
for (unsigned int i = 0; i < split_subsections.size(); ++i) {
std::string sub = hz::string_trim_copy(split_subsections[i], "\t\n\r"); // don't trim space
if (pcrecpp::RE("^ ").PartialMatch(sub) || pcrecpp::RE("^Error [0-9]+").PartialMatch(sub)) {
if (app_pcre_re("^ ").PartialMatch(sub) || app_pcre_re("^Error [0-9]+").PartialMatch(sub)) {
if (!subsections.empty()) {
subsections.back() += "\n\n" + sub; // append to previous part
} else {
@@ -525,20 +600,14 @@ bool SmartctlParser::parse_section_data_subsection_health(const std::string& sub
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_health;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
pcrecpp::RE re("^([^:\\n]+):[ \\t]*(.*)$", re_options);
std::string name, value;
if (re.PartialMatch(sub, &name, &value)) {
if (app_pcre_match("/^([^:\\n]+):[ \\t]*(.*)$/mi", sub, &name, &value)) {
hz::string_trim(name);
hz::string_trim(value);
// only one attribute in this section
if (app_pcre_match("/SMART overall-health self-assessment/mi", name)) {
pt.set_name(name, "overall_health", "Overall health self-assessment test");
pt.set_name(name, "overall_health", "Overall Health Self-Assessment Test");
pt.reported_value = value;
pt.value_type = StorageProperty::value_type_string;
pt.value_string = pt.reported_value;
@@ -563,16 +632,12 @@ bool SmartctlParser::parse_section_data_subsection_capabilities(const std::strin
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_capabilities;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
std::string sub = sub_initial;
// Fix some bugs in smartctl output (pre-5.39-final versions):
// There is a stale newline in "is in a Vendor Specific state\n.\n" and
// "is in a Reserved state\n.\n".
// pcrecpp::RE("\\n\\.$", re_options).GlobalReplace(".", &sub);
// app_pcre_replace("/\\n\\.$/mi", ".", &sub);
app_pcre_replace("/(is in a Vendor Specific state)\\n\\.$/mi", "\\1.", sub);
app_pcre_replace("/(is in a Reserved state)\\n\\.$/mi", "\\1.", sub);
@@ -608,8 +673,7 @@ bool SmartctlParser::parse_section_data_subsection_capabilities(const std::strin
// parse each block
pcrecpp::RE re("([^:]*):\\s*\\(([^)]+)\\)\\s*(.*)",
pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
pcrecpp::RE re = app_pcre_re("/([^:]*):\\s*\\(([^)]+)\\)\\s*(.*)/ms");
bool cap_found = false; // found at least one capability
@@ -676,7 +740,7 @@ bool SmartctlParser::parse_section_data_subsection_capabilities(const std::strin
p.value_type = StorageProperty::value_type_capability;
p.value_capability.reported_flag_value = numvalue_orig;
p.value_capability.flag_value = numvalue; // full flag value
p.value_capability.flag_value = static_cast<int16_t>(numvalue); // full flag value
p.value_capability.reported_strvalue = strvalue_orig;
// split capability lines into a vector. every flag sentence ends with "."
@@ -708,9 +772,6 @@ bool SmartctlParser::parse_section_data_subsection_capabilities(const std::strin
// Check the capabilities for internal properties we can use.
bool SmartctlParser::parse_section_data_internal_capabilities(StorageProperty& cap)
{
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// Some special capabilities we're interested in.
// Note: Smartctl gradually changed spelling Off-line to Offline in some messages.
@@ -721,29 +782,36 @@ bool SmartctlParser::parse_section_data_internal_capabilities(StorageProperty& c
// "was never started", "was completed without error", "is in progress",
// "was suspended by an interrupting command from host", etc...
pcrecpp::RE re_offline_status("^(Off-?line data collection) activity (?:is|was) (.*)$", re_options);
pcrecpp::RE re_offline_status = app_pcre_re("/^(Off-?line data collection) activity (?:is|was) (.*)$/mi");
// "Enabled", "Disabled". May not show up on older smartctl (< 5.1.10), so no way of knowing there.
pcrecpp::RE re_offline_enabled("^(Auto Off-?line Data Collection):[ \\t]*(.*)$", re_options);
pcrecpp::RE re_offline_immediate("^(SMART execute Off-?line immediate)$", re_options);
pcrecpp::RE re_offline_enabled = app_pcre_re("/^(Auto Off-?line Data Collection):[ \\t]*(.*)$/mi");
pcrecpp::RE re_offline_immediate = app_pcre_re("/^(SMART execute Off-?line immediate)$/mi");
// "No Auto Offline data collection support.", "Auto Offline data collection on/off support.".
pcrecpp::RE re_offline_auto("^(No |)(Auto Off-?line data collection (?:on\\/off )?support)$", re_options);
pcrecpp::RE re_offline_auto = app_pcre_re("/^(No |)(Auto Off-?line data collection (?:on\\/off )?support)$/mi");
// Same as above (smartctl <= 5.1-18). "No Automatic timer ON/OFF support."
pcrecpp::RE re_offline_auto2("^(No |)(Automatic timer ON\\/OFF support)$", re_options);
pcrecpp::RE re_offline_suspend("^(?:Suspend|Abort) (Off-?line collection upon new command)$", re_options);
pcrecpp::RE re_offline_surface("^(No |)(Off-?line surface scan supported)$", re_options);
pcrecpp::RE re_offline_time("^(Total time to complete Off-?line data collection)", re_options); // match on name!
pcrecpp::RE re_offline_auto2 = app_pcre_re("/^(No |)(Automatic timer ON\\/OFF support)$/mi");
pcrecpp::RE re_offline_suspend = app_pcre_re("/^(?:Suspend|Abort) (Off-?line collection upon new command)$/mi");
pcrecpp::RE re_offline_surface = app_pcre_re("/^(No |)(Off-?line surface scan supported)$/mi");
pcrecpp::RE re_selftest_status("^Self-test execution status", re_options); // match on name
pcrecpp::RE re_selftest_support("^(No |)(Self-test supported)$", re_options);
pcrecpp::RE re_conv_selftest_support("^(No |)(Conveyance Self-test supported)$", re_options);
pcrecpp::RE re_selective_selftest_support("^(No |)(Selective Self-test supported)$", re_options);
pcrecpp::RE re_selftest_short_time("^(Short self-test routine recommended polling time)", re_options); // match on name!
pcrecpp::RE re_selftest_long_time("^(Extended self-test routine recommended polling time)", re_options); // match on name!
pcrecpp::RE re_conv_selftest_time("^(Conveyance self-test routine recommended polling time)", re_options); // match on name!
pcrecpp::RE re_selftest_support = app_pcre_re("/^(No |)(Self-test supported)$/mi");
pcrecpp::RE re_conv_selftest_support = app_pcre_re("/^(No |)(Conveyance Self-test supported)$/mi");
pcrecpp::RE re_selective_selftest_support = app_pcre_re("/^(No |)(Selective Self-test supported)$/mi");
pcrecpp::RE re_sct_status("^(SCT Status supported)$", re_options);
pcrecpp::RE re_sct_control("^(SCT Feature Control supported)$", re_options); // means can change logging interval
pcrecpp::RE re_sct_data("^(SCT Data Table supported)$", re_options);
pcrecpp::RE re_sct_status = app_pcre_re("/^(SCT Status supported)$/mi");
pcrecpp::RE re_sct_control = app_pcre_re("/^(SCT Feature Control supported)$/mi"); // means can change logging interval
pcrecpp::RE re_sct_data = app_pcre_re("/^(SCT Data Table supported)$/mi");
// these are matched on name
pcrecpp::RE re_offline_status_group = app_pcre_re("/^(Off-?line data collection status)/mi");
pcrecpp::RE re_offline_time = app_pcre_re("/^(Total time to complete Off-?line data collection)/mi");
pcrecpp::RE re_offline_cap_group = app_pcre_re("/^(Off-?line data collection capabilities)/mi");
pcrecpp::RE re_smart_cap_group = app_pcre_re("/^(SMART capabilities)/mi");
pcrecpp::RE re_error_log_cap_group = app_pcre_re("/^(Error logging capability)/mi");
pcrecpp::RE re_sct_cap_group = app_pcre_re("/^(SCT capabilities)/mi");
pcrecpp::RE re_selftest_status = app_pcre_re("/^Self-test execution status/mi");
pcrecpp::RE re_selftest_short_time = app_pcre_re("/^(Short self-test routine recommended polling time)/mi");
pcrecpp::RE re_selftest_long_time = app_pcre_re("/^(Extended self-test routine recommended polling time)/mi");
pcrecpp::RE re_conv_selftest_time = app_pcre_re("/^(Conveyance self-test routine recommended polling time)/mi");
if (cap.section != StorageProperty::section_data || cap.subsection != StorageProperty::subsection_capabilities) {
debug_out_error("app", DBG_FUNC_MSG << "Non-capability property passed.\n");
@@ -751,9 +819,30 @@ bool SmartctlParser::parse_section_data_internal_capabilities(StorageProperty& c
}
// Name the capability groups for easy matching when setting descriptions
if (cap.value_type == StorageProperty::value_type_capability) {
if (re_offline_status_group.PartialMatch(cap.reported_name)) {
cap.generic_name = "offline_status_group";
} else if (re_offline_cap_group.PartialMatch(cap.reported_name)) {
cap.generic_name = "offline_cap_group";
} else if (re_smart_cap_group.PartialMatch(cap.reported_name)) {
cap.generic_name = "smart_cap_group";
} else if (re_error_log_cap_group.PartialMatch(cap.reported_name)) {
cap.generic_name = "error_log_cap_group";
} else if (re_sct_cap_group.PartialMatch(cap.reported_name)) {
cap.generic_name = "sct_cap_group";
} else if (re_selftest_status.PartialMatch(cap.reported_name)) {
cap.generic_name = "last_selftest_cap_group";
}
}
// match on name:
// Last self-test status
if (re_selftest_status.PartialMatch(cap.reported_name)) {
// The last self-test status. break up into pieces.
@@ -852,6 +941,7 @@ bool SmartctlParser::parse_section_data_internal_capabilities(StorageProperty& c
return true;
}
// Extract subcapabilities from capability vectors and assign to "internal" section.
if (cap.value_type == StorageProperty::value_type_capability) {
@@ -955,32 +1045,53 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_attributes;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// split to lines
std::vector<std::string> lines;
hz::string_split(sub, '\n', lines, true);
// Format notes:
// * Before 5.1-14, no UPDATED column was present.
// * Before 5.1-14, no UPDATED column was present in "old" format.
// * Most, but not all attribute names are with underscores. However, I encountered one
// named "Head flying hours". So, parse until we encounter the next column.
// * One WD drive had non-integer flags, something like "PO--C-", with several
// lines of their descriptions after the attributes block (each line started with spaces and |).
// named "Head flying hours" and there are slashes sometimes as well.
// So, parse until we encounter the next column.
// * "Old" format: One WD drive had non-integer flags, something like "PO--C-", with several
// lines of their descriptions after the attributes block (each line started with spaces and |)
// (seems to be a CVS version from 2006).
// * SSD drives may show "---" in value/worst/threshold fields.
// "old" format (used in -a):
// "ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE"
// " 1 Raw_Read_Error_Rate 0x000f 115 099 006 Pre-fail Always - 90981479"
// " 12 Power_Cycle_Count 0x0000 --- --- --- Old_age Offline - 167"
// "brief" format (used in -x):
// "ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE"
// " 1 Raw_Read_Error_Rate POSR-- 118 099 006 - 174772734"
enum {
FormatStyleOld,
FormatStyleNoUpdated, // old format without UPDATED column
FormatStyleBrief
};
bool attr_found = false; // at least one attribute was found
bool attr_format_with_updated = false; // UPDATED column present
int attr_format_style = FormatStyleOld;
pcrecpp::RE re_up("[ \\t]*([0-9]+) ([^\\t\\n]+)[ \\t]+((?:0x[a-fA-F0-9]+)|(?:[A-Z-]{2,}))[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+"
"([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*", re_options);
std::string space_re = "[ \\t]+";
pcrecpp::RE re_noup("[ \\t]*([0-9]+) ([^\\t\\n]+)[ \\t]+((?:0x[a-fA-F0-9]+)|(?:[A-Z-]{2,}))[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+"
"([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*", re_options);
std::string flag_re = "((?:0x[a-fA-F0-9]+)|(?:[A-Z+-]{2,}))";
std::string base_re = "[ \\t]*([0-9]+) ([^\\t\\n]+)" + space_re + flag_re + space_re // name / flag
+ "([0-9-]+)" + space_re + "([0-9-]+)" + space_re + "([0-9-]+)" + space_re; // value / worst / threshold
std::string type_re = "([^ \\t\\n]+)" + space_re;
std::string updated_re = "([^ \\t\\n]+)" + space_re;
std::string failed_re = "([^ \\t\\n]+)" + space_re;
std::string raw_re = "(.+)[ \\t]*";
pcrecpp::RE re_flag_descr("^[\\t ]+\\|", re_options);
pcrecpp::RE re_old_up = app_pcre_re("/" + base_re + type_re + updated_re + failed_re + raw_re + "/mi");
pcrecpp::RE re_old_noup = app_pcre_re("/" + base_re + type_re + failed_re + raw_re + "/mi");
pcrecpp::RE re_brief = app_pcre_re("/" + base_re + failed_re + raw_re + "/mi");
pcrecpp::RE re_flag_descr = app_pcre_re("/^[\\t ]+\\|/mi");
for(unsigned int i = 0; i < lines.size(); ++i) {
@@ -991,7 +1102,12 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
continue;
if (app_pcre_match("/ATTRIBUTE_NAME/mi", line)) {
attr_format_with_updated = (app_pcre_match("/UPDATED/mi", line));
// detect format type
if (!app_pcre_match("/WHEN_FAILED/mi", line)) {
attr_format_style = FormatStyleBrief;
} else if (!app_pcre_match("/UPDATED/mi", line)) {
attr_format_style = FormatStyleNoUpdated;
}
continue; // we don't need this line
}
@@ -1000,7 +1116,7 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
}
if (app_pcre_match("/Data Structure revision number/mi", line)) {
pcrecpp::RE re("^([^:\\n]+):[ \\t]*(.*)$", re_options);
pcrecpp::RE re = app_pcre_re("/^([^:\\n]+):[ \\t]*(.*)$/mi");
std::string name, value;
if (re.PartialMatch(line, &name, &value)) {
hz::string_trim(name);
@@ -1009,7 +1125,7 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
hz::string_is_numeric(value, value_num, false);
StorageProperty p(pt);
p.set_name(name);
p.set_name(name, "data_structure_version");
p.reported_value = value;
p.value_type = StorageProperty::value_type_integer;
p.value_integer = value_num;
@@ -1026,15 +1142,22 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
bool matched = true;
if (attr_format_with_updated) {
if (!re_up.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type,
if (attr_format_style == FormatStyleOld) {
if (!re_old_up.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type,
&update_type, &when_failed, &raw_value)) {
matched = false;
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n");
}
} else { // no UPDATED column
if (!re_noup.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type,
} else if (attr_format_style == FormatStyleNoUpdated) {
if (!re_old_noup.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type,
&when_failed, &raw_value)) {
matched = false;
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n");
}
} else if (attr_format_style == FormatStyleBrief) {
if (!re_brief.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold,
&when_failed, &raw_value)) {
matched = false;
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n");
@@ -1052,23 +1175,49 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string&
StorageAttribute a;
hz::string_is_numeric(hz::string_trim_copy(id), a.id, true, 10);
a.flag = hz::string_trim_copy(flag);
hz::string_is_numeric(hz::string_trim_copy(value), a.value, true, 10);
hz::string_is_numeric(hz::string_trim_copy(worst), a.worst, true, 10);
hz::string_is_numeric(hz::string_trim_copy(threshold), a.threshold, true, 10);
uint8_t norm_value = 0, worst_value = 0, threshold_value = 0;
a.attr_type = (attr_type == "Pre-fail" ? StorageAttribute::attr_type_prefail
: (attr_type == "Old_age" ? StorageAttribute::attr_type_oldage : StorageAttribute::attr_type_unknown));
if (hz::string_is_numeric(hz::string_trim_copy(value), norm_value, true, 10)) {
a.value = norm_value;
}
if (hz::string_is_numeric(hz::string_trim_copy(worst), worst_value, true, 10)) {
a.worst = worst_value;
}
if (hz::string_is_numeric(hz::string_trim_copy(threshold), threshold_value, true, 10)) {
a.threshold = threshold_value;
}
a.update_type = (update_type == "Always" ? StorageAttribute::update_type_always
: (update_type == "Offline" ? StorageAttribute::update_type_offline : StorageAttribute::update_type_unknown));
if (attr_format_style == FormatStyleBrief) {
a.attr_type = app_pcre_match("/P/", a.flag) ? StorageAttribute::attr_type_prefail : StorageAttribute::attr_type_oldage;
} else {
if (attr_type == "Pre-fail") {
a.attr_type = StorageAttribute::attr_type_prefail;
} else if (attr_type == "Old_age") {
a.attr_type = StorageAttribute::attr_type_oldage;
} else {
a.attr_type = StorageAttribute::attr_type_unknown;
}
}
if (attr_format_style == FormatStyleBrief) {
a.update_type = app_pcre_match("/O/", a.flag) ? StorageAttribute::update_type_always : StorageAttribute::update_type_offline;
} else {
if (update_type == "Always") {
a.update_type = StorageAttribute::update_type_always;
} else if (update_type == "Offline") {
a.update_type = StorageAttribute::update_type_offline;
} else {
a.update_type = StorageAttribute::update_type_unknown;
}
}
a.when_failed = StorageAttribute::fail_time_unknown;
hz::string_trim(when_failed);
if (when_failed == "-") {
a.when_failed = StorageAttribute::fail_time_none;
} else if (when_failed == "In_the_past") {
} else if (when_failed == "In_the_past" || when_failed == "Past") { // the second one if from brief format
a.when_failed = StorageAttribute::fail_time_past;
} else if (when_failed == "FAILING_NOW") {
} else if (when_failed == "FAILING_NOW" || when_failed == "NOW") { // the second one if from brief format
a.when_failed = StorageAttribute::fail_time_now;
}
@@ -1104,10 +1253,6 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_error_log;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// Note: The format of this section was changed somewhere between 5.0-x and 5.30.
// The old format is doesn't really give any useful info, and whatever's left is somewhat
// parsable by this parser. Can't really improve that.
@@ -1115,9 +1260,9 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
bool data_found = false;
// error log version
// Error log version
{
pcrecpp::RE re("^(SMART Error Log Version):[ \\t]*(.*)$", re_options);
pcrecpp::RE re = app_pcre_re("/^(SMART Error Log Version):[ \\t]*(.*)$/mi");
std::string name, value;
if (re.PartialMatch(sub, &name, &value)) {
@@ -1138,11 +1283,24 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
}
}
// error log count
// Error log support
{
pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Error Logging)$/mi");
if (re.PartialMatch(sub)) {
StorageProperty p(pt);
p.set_name("error_log_unsupported");
p.readable_name = "Warning";
p.readable_value = "Device does not support error logging";
add_property(p);
}
}
// Error log enty count
{
// note: these represent the same information
pcrecpp::RE re1("^ATA Error Count:[ \\t]*([0-9]+)", re_options);
pcrecpp::RE re2("^No Errors Logged$", re_options);
pcrecpp::RE re1 = app_pcre_re("/^ATA Error Count:[ \\t]*([0-9]+)/mi");
pcrecpp::RE re2 = app_pcre_re("/^No Errors Logged$/mi");
std::string value;
if (re1.PartialMatch(sub, &value) || re2.PartialMatch(sub)) {
@@ -1167,11 +1325,14 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
// individual errors
{
// split by blocks
pcrecpp::RE re_block("^((Error[ \\t]*([0-9]+))[ \\t]*occurred at disk power-on lifetime:[ \\t]*([0-9]+) hours.*(?:\\n(?: |\\n ).*)*)", re_options);
pcrecpp::RE re_block = app_pcre_re("/^((Error[ \\t]*([0-9]+))[ \\t]*occurred at disk power-on lifetime:[ \\t]*([0-9]+) hours.*(?:\\n(?: |\\n ).*)*)/mi");
// for "in an unknown state" - remove first two words.
pcrecpp::RE re_state("occurred, the device was[ \\t]*(?: in)?(?: an?)?[ \\t]+([^.\\n]*)\\.?", re_options);
pcrecpp::RE re_type("[ \\t]+Error:[ \\t]*([^ \\t\\n]+)[ \\t]*(.*)$", re_options);
// " When the command that caused the error occurred, the device was active or idle."
// Note: For "in an unknown state" - remove first two words.
pcrecpp::RE re_state = app_pcre_re("/occurred, the device was[ \\t]*(?: in)?(?: an?)?[ \\t]+([^.\\n]*)\\.?/mi");
// " 84 51 2c 71 cd 3f e6 Error: ICRC, ABRT 44 sectors at LBA = 0x063fcd71 = 104844657"
// " 40 51 00 f5 41 61 e0 Error: UNC at LBA = 0x006141f5 = 6373877"
pcrecpp::RE re_type = app_pcre_re("/[ \\t]+Error:[ \\t]*([ ,a-z]+)[ \\t]+((?:[0-9]+|at )[ \\t]*.*)$/mi");
std::string block, name, value_num, value_time;
pcrecpp::StringPiece input(sub); // position tracker
@@ -1182,9 +1343,9 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
hz::string_trim(value_time);
// debug_out_dump("app", "\nBLOCK -------------------------------\n" << block);
std::string state, etype, emore;
std::string state, etypes_str, emore;
re_state.PartialMatch(block, &state);
re_type.PartialMatch(block, &etype, &emore);
re_type.PartialMatch(block, &etypes_str, &emore);
StorageProperty p(pt);
p.set_name(hz::string_trim_copy(name)); // "Error 6"
@@ -1194,8 +1355,14 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string&
hz::string_is_numeric(value_num, p.value_error_block.error_num, false);
hz::string_is_numeric(value_time, p.value_error_block.lifetime_hours, false);
std::vector<std::string> etypes;
hz::string_split(etypes_str, ",", etypes, true);
for (std::vector<std::string>::iterator iter = etypes.begin(); iter != etypes.end(); ++iter) {
hz::string_trim(*iter);
}
p.value_error_block.device_state = hz::string_trim_copy(state);
p.value_error_block.reported_type = hz::string_trim_copy(etype);
p.value_error_block.reported_types = etypes;
p.value_error_block.type_more_info = hz::string_trim_copy(emore);
add_property(p);
@@ -1234,9 +1401,6 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_selftest_log;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// Self-test log contains:
// * structure revision number
// * a list of current / previous tests performed, with each having:
@@ -1249,7 +1413,7 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
bool data_found = false; // true if something was found.
// the whole subsection
// The whole subsection
{
StorageProperty p(pt);
p.set_name("SMART Self-test log", "selftest_log");
@@ -1262,12 +1426,25 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
}
// self-test log version
// Self-test log support
{
pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Self Test Logging)$/mi");
if (re.PartialMatch(sub)) {
StorageProperty p(pt);
p.set_name("selftest_log_unsupported");
p.readable_name = "Warning";
p.readable_value = "Device does not support self-test logging";
add_property(p);
}
}
// Self-test log version
{
// newer smartctl (since smartctl 5.1-16)
pcrecpp::RE re1("(SMART Self-test log structure[^\\n0-9]*)([^ \\n]+)[ \\t]*$", re_options);
pcrecpp::RE re1 = app_pcre_re("/(SMART Self-test log structure[^\\n0-9]*)([^ \\n]+)[ \\t]*$/mi");
// older smartctl
pcrecpp::RE re2("(SMART Self-test log, version number[^\\n0-9]*)([^ \\n]+)[ \\t]*$", re_options);
pcrecpp::RE re2 = app_pcre_re("/(SMART Self-test log, version number[^\\n0-9]*)([^ \\n]+)[ \\t]*$/mi");
std::string name, value;
if (re1.PartialMatch(sub, &name, &value) || re2.PartialMatch(sub, &name, &value)) {
@@ -1295,9 +1472,9 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
// individual entries
{
// split by columns (minimum 2 spaces between them)
// num, type, status, remaining, hours, lba
pcrecpp::RE re("^(#[ \\t]*([0-9]+)[ \\t]*(\\S.*) [ \\t]*(\\S.*) [ \\t]*([0-9]+%) [ \\t]*([0-9]+) [ \\t]*(\\S.*))$", re_options);
// split by columns.
// num, type, status, remaining, hours, lba (optional).
pcrecpp::RE re = app_pcre_re("/^(#[ \\t]*([0-9]+)[ \\t]+(\\S+(?: \\S+)*) [ \\t]*(\\S.*) [ \\t]*([0-9]+%) [ \\t]*([0-9]+)[ \\t]*((?: [ \\t]*\\S.*)?))$/mi");
std::string line, num, type, status_str, remaining, hours, lba;
pcrecpp::StringPiece input(sub); // position tracker
@@ -1316,6 +1493,9 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
p.value_selftest_entry.type = hz::string_trim_copy(type);
p.value_selftest_entry.lba_of_first_error = hz::string_trim_copy(lba);
// old smartctls didn't print anything for lba if none, but newer ones print "-". normalize.
if (p.value_selftest_entry.lba_of_first_error == "")
p.value_selftest_entry.lba_of_first_error = "-";
hz::string_trim(status_str);
StorageSelftestEntry::status_t status = StorageSelftestEntry::status_unknown;
@@ -1384,10 +1564,6 @@ bool SmartctlParser::parse_section_data_subsection_selective_selftest_log(const
pt.section = StorageProperty::section_data;
pt.subsection = StorageProperty::subsection_selective_selftest_log;
pcrecpp::RE_Options re_options;
re_options.set_caseless(true).set_multiline(true);
// Selective self-test log contains:
// * 5 (maybe less/more?) test entries, each with
// span / min_lba / max_lba / current_test_status.
+4 -5
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -28,13 +28,12 @@ class SmartctlParser {
bool parse_full(const std::string& s);
// supply output of "smartctl --version" here.
// returns empty string on failure.
static std::string parse_version(const std::string& s);
// 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_version(const std::string& version_str);
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]
+2 -2
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#undef HZ_USE_LIBDEBUG
+6 -11
View File
@@ -1,19 +1,17 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#include <iostream>
#include <glibmm.h>
#include <glib.h>
int main(int argc, char** argv)
{
Glib::thread_init();
// g_thread_init(NULL);
GPid pid;
int fd_stdout = 0, fd_stderr = 0;
@@ -44,7 +42,6 @@ int main(int argc, char** argv)
#ifdef _WIN32
GIOChannel* channel_stdout = g_io_channel_win32_new_fd(fd_stdout);
GIOChannel* channel_stderr = g_io_channel_win32_new_fd(fd_stderr);
@@ -54,24 +51,22 @@ int main(int argc, char** argv)
#endif
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
GIOFlags flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
int channel_flags = ~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, flags, 0);
g_io_channel_set_flags(channel_stdout, GIOFlags(g_io_channel_get_flags(channel_stdout) & channel_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, flags, 0);
g_io_channel_set_flags(channel_stderr, GIOFlags(g_io_channel_get_flags(channel_stderr) & channel_flags), 0);
g_io_channel_set_buffer_size(channel_stderr, 10000);
}
g_main_loop_run(g_main_loop_new(NULL, false));
+29 -572
View File
@@ -1,597 +1,55 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "hz/hz_config.h" // CONFIG_*
#include <ios> // std::boolalpha
// #include <glibmm/fileutils.h> // Dir
#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>
#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
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;
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, but \\.\PhysicalDriveN
// seems to be the most native and acceptable.
inline std::string detect_drives_win32(std::vector<std::string>& devices)
{
for (int drive_num = 0; ; ++drive_num) {
// If the drive is openable, then it's there.
// NOTE: Administrative privileges are required to open it.
std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num);
HANDLE h = CreateFile(name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
// The numbers seem to be continuous, so break on first invalid.
if (h == INVALID_HANDLE_VALUE)
break;
CloseHandle (h);
// smartctl uses /dev/pdN for \\.\PhysicalDrive N.
devices.push_back(hz::string_sprintf("/dev/pd%d", drive_num));
}
return std::string();
}
#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)
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n");
std::vector<std::string> devices;
std::vector<StorageDeviceRefPtr> all_detected;
std::string error_msg;
bool found = false;
// Try each one and move to next if it fails.
#if defined CONFIG_KERNEL_LINUX
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;
}
}
error_msg = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback.
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_WINDOWS
if (!found) {
error_msg = detect_drives_win32(devices); // win32
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#elif defined CONFIG_KERNEL_FAMILY_WINDOWS
error_msg = detect_drives_win32(all_detected, ex_factory); // win32
#else // freebsd, etc...
if (!found) {
error_msg = detect_drives_dev(devices); // bsd, etc... . scans /dev.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
error_msg = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev.
#endif
if (!found) {
if (all_detected.empty()) {
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<std::string>::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) {
std::string dev = *iter;
for (std::vector<StorageDeviceRefPtr>::iterator iter = all_detected.begin(); iter != all_detected.end(); ++iter) {
StorageDeviceRefPtr drive = *iter;
// try to match against patterns
// for (unsigned int i = 0; i < match_patterns_.size(); i++) {
@@ -602,21 +60,20 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
// matched, check the blacklist
bool blacked = false;
for (unsigned int j = 0; j < blacklist_patterns_.size(); j++) {
if (app_pcre_match(blacklist_patterns_[j], dev)) { // matched the blacklist too
if (app_pcre_match(blacklist_patterns_[j], drive->get_device())) { // matched the blacklist too
blacked = true;
break;
}
}
StorageDeviceRefPtr drive(new StorageDevice(dev));
debug_out_info("app", "Found device: \"" << drive->get_device() << "\".\n");
debug_out_info("app", "Found device: " << drive->get_device_with_type() << ".\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() << "\" is blacklisted, ignoring.\n");
debug_out_info("app", "Device " << drive->get_device_with_type() << " is blacklisted, ignoring.\n");
// break; // go to next device
}
// }
@@ -630,27 +87,26 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> smartctl_ex, bool return_first_error)
ExecutorFactoryRefPtr ex_factory, bool return_first_error)
{
fetch_data_errors_.clear();
fetch_data_error_outputs_.clear();
// 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
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
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() + "...");
smartctl_ex->set_running_msg("Running %s on " + drive->get_device_with_type() + "...");
// 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 = drive->fetch_basic_data_and_parse(smartctl_ex);
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);
}
// normally we skip drives with errors - possibly scsi, etc...
if (return_first_error && !error_msg.empty())
@@ -665,9 +121,10 @@ 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() << ":\n"
debug_out_dump("app", "Device information for " << drive->get_device()
<< " (type: \"" << drive->get_type_argument() << "\"):\n"
<< "\tModel: " << drive->get_model_name() << "\n"
<< "\tType: " << StorageDevice::get_type_name(drive->get_type()) << "\n"
<< "\tDetected type: " << StorageDevice::get_type_readable_name(drive->get_detected_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n"
);
@@ -679,12 +136,12 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>&
std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> smartctl_ex)
ExecutorFactoryRefPtr ex_factory)
{
std::string error_msg = detect(put_drives_here);
std::string error_msg = detect(put_drives_here, ex_factory);
if (error_msg.empty())
fetch_basic_data(put_drives_here, smartctl_ex, false); // ignore its errors, there may be plenty of them.
fetch_basic_data(put_drives_here, ex_factory, false); // ignore its errors, there may be plenty of them.
return error_msg;
}
+6 -4
View File
@@ -1,6 +1,6 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
@@ -14,6 +14,7 @@
#include "storage_device.h"
#include "cmdex_sync.h"
#include "executor_factory.h"
@@ -28,17 +29,18 @@ class StorageDetector {
// detects a list of drives. returns detection error if error occurs.
std::string detect(std::vector<StorageDeviceRefPtr>& put_drives_here);
std::string detect(std::vector<StorageDeviceRefPtr>& put_drives_here,
ExecutorFactoryRefPtr ex_factory);
// fetch basic data of "drives" elements
std::string fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> smartctl_ex = 0, bool return_first_error = false);
ExecutorFactoryRefPtr ex_factory, bool return_first_error = false);
// do both of the above, return detection error.
std::string detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
ExecutorFactoryRefPtr ex_factory);
// void add_match_patterns(std::vector<std::string>& patterns)
+159
View File
@@ -0,0 +1,159 @@
/**************************************************************************
Copyright:
(C) 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#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"
/// Get number of ports using tw_cli. Return -1 on error.
inline std::string tw_cli_get_drives(const std::string& dev, int scsi_host_no,
std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev)
{
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::string command_options = hz::string_sprintf("/c%d show all", scsi_host_no);
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
std::string 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.";
}
// split to lines
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
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(scsi_host_no) + "/p" + hz::number_to_string(port))));
} else {
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "3ware," + hz::number_to_string(port))));
}
}
}
}
}
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)
{
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)), "show");
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
std::string 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.";
}
// 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) {
controllers.push_back(controller);
}
}
}
return std::string();
}
#endif
+497
View File
@@ -0,0 +1,497 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "storage_detector_linux.h"
#if defined CONFIG_KERNEL_LINUX
#include <algorithm> // std::find
#include <cstdio> // std::fgets(), std::FILE
#include <cerrno> // ENXIO
#include "hz/debug.h"
#include "hz/fs_path_utils.h"
#include "hz/fs_file.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "storage_detector_helpers.h"
namespace {
// 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
*/
/*
// We don't use udev anymore - not all distros have it, and e.g. Ubuntu
// has it all wrong (two symlinks (sda, sdb) pointing both to sdb).
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.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_file(hz::File& file, std::vector<std::string>& lines)
{
if (!file.open("rb")) // closed automatically
return false; // the error message is in File itself.
std::FILE* fp = file.get_handle();
if (!fp)
return false;
char line[256];
while (std::fgets(line, static_cast<int>(sizeof(line)), fp) != NULL) {
if (*line != '\0')
lines.push_back(line);
}
return true;
}
// Read /proc/partitions file. Return error message on error.
inline std::string read_proc_partitions_file(std::vector<std::string>& lines)
{
std::string path;
if (!rconfig::get_data("system/linux_proc_partitions_path", path) || path.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(path);
if (!read_proc_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;
}
return std::string();
}
// Read /proc/partitions file. Return error message on error.
inline std::string read_proc_devices_file(std::vector<std::string>& lines)
{
std::string path;
if (!rconfig::get_data("system/linux_proc_devices_path", path) || path.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Devices file path is not set.\n");
return "Devices file path is not set.";
}
hz::File file(path);
if (!read_proc_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 << "Devices file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Devices file exists but cannot be read.\n");
}
return error_msg;
}
return std::string();
}
// Read /proc/partitions file. Return error message on error.
inline std::string read_proc_scsi_scsi_file(std::vector<std::string>& lines)
{
std::string path;
if (!rconfig::get_data("system/linux_proc_scsi_scsi_path", path) || path.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "SCSI file path is not set.\n");
return "SCSI file path is not set.";
}
hz::File file(path);
if (!read_proc_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 << "SCSI file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "SCSI file exists but cannot be read.\n");
}
return error_msg;
}
return std::string();
}
/// Get number of ports by sequentially running smartctl on each port, until
/// one of the gives an error. Return -1 on error.
inline std::string smartctl_get_drives(const std::string& dev, const std::string& type,
int from, int to, std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
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));
// std::string error_msg = execute_smartctl(dev, "-d " + type_arg, "-i", smartctl_ex, output);
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
std::string 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", output)) {
break;
}
if (!error_msg.empty()) {
debug_out_info("app", "Smartctl returned with an error: " << error_msg);
} else {
drives.push_back(drive);
}
}
return std::string();
}
// 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 freebsd, solaris or 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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /proc/partitions...\n");
std::vector<std::string> lines;
std::string error_msg = read_proc_partitions_file(lines);
if (!error_msg.empty()) {
return error_msg;
}
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
blacklist.push_back("/dm-[0-9]*$/"); // linux device mapper
std::vector<std::string> devices;
for (std::size_t 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);
}
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
for (std::size_t i = 0; i < devices.size(); ++i) {
StorageDeviceRefPtr drive(new StorageDevice(devices.at(i)));
drive->fetch_basic_data_and_parse(smartctl_ex);
// 3ware controllers also export themselves as sd*. Smartctl detects that,
// so we can avoid adding them. Older smartctl (5.38) prints "AMCC", newer one
// prints "AMCC/3ware controller". It's better to search it this way.
if (!app_pcre_match("/try adding '-d 3ware,N'/im", drive->get_info_output())) {
drives.push_back(drive);
}
}
return std::string();
}
/**
Detect drives behind 3ware RAID controller.
3ware Linux:
Call as: smartctl -i -d 3ware,[0-127] /dev/twa[0-15] (or twe[0-15])
Use twe* for [678]xxx series, and twa* for 9xxx series.
Note: twe* devices are limited to [0-15] ports (not sure about this).
Note: /dev/tw* devices may not exist, they are created by smartctl on the first run.
Note: for twe*, /dev/sda may also exist (to be used with -d 3ware,N), we should
somehow detect and ignore them.
Note: when specifying non-existent port, either a "Device Read Identity Failed"
error, or a "blank" info may be returned.
Detection:
Grep /proc/devices for "twa" or "twe" (e.g. "251 twa"). Use this for /dev/tw* part.
Grep /proc/scsi/scsi for AMCC or 3ware (LSI too?), use number of matched lines N
for /dev/tw*[0, N-1].
For detecting the number of ports, use "tw_cli /cK show all", K being the controller
scsi number, which is displayed as scsiK in the scsi file.
If there's no tw_cli, we'll have to scan all the ports (up to supported maximum).
This is too slow however, so scan only 24.
Implementation notes: it seems that twe uses "3ware" and twa uses "AMCC".
We can't handle a situation with both twa and twe present, since we don't know
how they will be ordered for tw_cli.
*/
inline std::string detect_drives_linux_3ware(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> lines;
std::string error_msg = read_proc_devices_file(lines);
if (!error_msg.empty()) {
return error_msg;
}
bool twa_found = false;
bool twe_found = false;
// Check /proc/devices for twa or twe
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string dev;
if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ae])/", hz::string_trim_copy(lines[i]), &dev)) {
debug_out_dump("app", DBG_FUNC_MSG << "Found " << dev << " entry in devices file.\n");
if (dev == "twa") {
twa_found = true;
} else if (dev == "twe") {
twe_found = true;
} else {
DBG_ASSERT(0); // error in regexp?
}
}
}
if (!twa_found && !twe_found) {
return std::string(); // no controllers
}
lines.clear();
error_msg = read_proc_scsi_scsi_file(lines);
if (!error_msg.empty()) {
return error_msg;
}
int num_controllers = 0;
int last_scsi_host = 0;
pcrecpp::RE host_re = app_pcre_re("^Host: scsi([0-9]+)");
for (std::size_t i = 0; i < lines.size(); ++i) {
// The format of this file is scsi host number on one line, vendor on another.
// debug_out_error("app", "SCSI Line: " << hz::string_trim_copy(lines[i]) << "\n");
std::string scsi_host_str;
if (host_re.PartialMatch(hz::string_trim_copy(lines[i]), &scsi_host_str)) {
// debug_out_dump("app", "SCSI Host " << scsi_host_str << " found.\n");
hz::string_is_numeric(scsi_host_str, last_scsi_host);
}
if (!app_pcre_match("/Vendor: (AMCC)|(3ware) /i", hz::string_trim_copy(lines[i]))) {
continue; // not a supported controller
}
debug_out_dump("app", "Found AMCC/3ware controller in SCSI file, SCSI host " << last_scsi_host << ".\n");
// We can't handle both twa and twe in one system, so assume twa by default.
// We can't map twaX to scsiY, so lets assume the relative order is the same.
std::string dev = std::string("/dev/") + (twa_found ? "twa" : "twe") + hz::number_to_string(num_controllers);
error_msg = tw_cli_get_drives(dev, last_scsi_host, drives, ex_factory, false);
if (!error_msg.empty()) { // no tw_cli
int max_ports = rconfig::get_data<int32_t>("system/linux_max_scan_ports");
max_ports = std::max(max_ports, 23); // 128 smartctl calls are too much (it's too slow). Settle for 24.
error_msg = smartctl_get_drives(dev, "3ware,%d", 0, max_ports, drives, ex_factory);
}
if (!error_msg.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get number of ports on a 3ware controller.\n");
}
++num_controllers;
}
if (num_controllers == 0) {
debug_out_warn("app", DBG_FUNC_MSG << "3ware entry found in devices file, but SCSI file contains no known entries.\n");
}
return error_msg;
}
} // anon ns
std::string detect_drives_linux(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> error_msgs;
std::string error_msg;
// 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).
// error_msg = detect_drives_linux_udev_byid(devices); // linux udev
error_msg = detect_drives_linux_proc_partitions(drives, ex_factory);
if (!error_msg.empty()) {
error_msgs.push_back(error_msg);
}
error_msg = detect_drives_linux_3ware(drives, ex_factory);
if (!error_msg.empty()) {
error_msgs.push_back(error_msg);
}
return hz::string_join(error_msgs, "\n");
}
#endif // CONFIG_KERNEL_LINUX
+30
View File
@@ -0,0 +1,30 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef STORAGE_DETECTOR_LINUX_H
#define STORAGE_DETECTOR_LINUX_H
#include <string>
#include <vector>
#include "hz/hz_config.h" // CONFIG_*
#include "executor_factory.h"
#include "storage_device.h"
#if defined CONFIG_KERNEL_LINUX
/// Detect drives in linux
std::string detect_drives_linux(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
#endif
+270
View File
@@ -0,0 +1,270 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "storage_detector_win32.h"
#if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
#if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
#include <util.h> // getrawpartition()
#endif
#include "hz/debug.h"
#include "hz/local_algo.h" // hz::shell_sort()
#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"
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting 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("/ ^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_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 (std::size_t i = 0; i < matched_devices.size(); ++i) {
devices.push_back(matched_devices[i]);
}
#endif
hz::shell_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
+28
View File
@@ -0,0 +1,28 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef STORAGE_DETECTOR_OTHER_H
#define STORAGE_DETECTOR_OTHER_H
#include <string>
#include <vector>
#include "hz/hz_config.h" // CONFIG_*
#include "executor_factory.h"
#include "storage_device.h"
#if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
// FreeBSD, Solaris, etc... .
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
#endif
+6 -4
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_unlicense.txt
***************************************************************************/
#include <iostream>
@@ -24,13 +24,15 @@ int main()
// sd.add_match_patterns(match_patterns);
sd.add_blacklist_patterns(blacklist_patterns);
std::string error_msg = sd.detect_and_fetch_basic_data(drives);
ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(false));
std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory);
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() << " (" << StorageDevice::get_type_name(drives[i]->get_type()) << ")\n";
std::cerr << drives[i]->get_device_with_type() <<
" (" << StorageDevice::get_type_readable_name(drives[i]->get_detected_type()) << ")\n";
}
}
+209
View File
@@ -0,0 +1,209 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "storage_detector_win32.h"
#if defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
#include <glibmm.h>
#include "hz/win32_tools.h"
#include "hz/string_sprintf.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "storage_detector_helpers.h"
/*
3ware Windows (XP so far, maybe the same under the others):
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.
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.
*/
/**
smartctl --scan-open output for win32 (3ware):
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
smartctl --scan-open output for linux:
/dev/sda -d sat # /dev/sda [SAT], ATA device
/dev/sdb -d sat # /dev/sdb [SAT], ATA device
/dev/sdc -d sat # /dev/sdc [SAT], ATA device
*/
namespace {
/// 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, std::vector<int>& equivalent_pds)
{
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 << "Error while executing smartctl binary.\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
pcrecpp::RE port_re = app_pcre_re("/^(/dev/[a-z0-9]+),([0-9]+)[ \\t]+-d[ \\t]+([^ \\t\\n]+)/i");
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 letter;
if (dev_re.PartialMatch(dev, &letter)) {
// don't use pd* devices equivalent to these sd* devices.
equivalent_pds.push_back(letter.at(0) - 'a');
}
std::string full_dev = dev + "," + port_str;
drives.push_back(StorageDeviceRefPtr(new StorageDevice(full_dev, type)));
}
}
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<int> used_pds;
std::string error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds);
bool multiport_found = !drives.empty();
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...).
// We don't use any long/unopenable files here, so use the ANSI version.
HANDLE h = CreateFileA(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);
if (std::find(used_pds.begin(), used_pds.end(), drive_num) == used_pds.end()) {
std::string dev = hz::string_sprintf("pd%d", drive_num);
drives.push_back(new StorageDevice(dev));
}
}
// 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 variant of smartctl.
if (!multiport_found) {
std::string inst_path;
hz::win32_get_registry_value_string(HKEY_USERS, ".DEFAULT\\Software\\3ware\\3DM2", "InstallPath", inst_path);
if (!inst_path.empty()) {
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);
}
}
}
return std::string();
}
#endif // CONFIG_KERNEL_FAMILY_WINDOWS

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