Compare commits

...
2 Commits
Author SHA1 Message Date
Alexander Shaduri 042111b3a2 Tagging 0.8.0rc4:
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).
    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.
    Improved .desktop files.
    License for tests and examples is legally more correct now.
    Minor bugfixes.
2010-12-26 18:54:42 +00:00
Alexander Shaduri 3a181b6880 Imported 0.8.0rc4. 2010-12-26 18:54:11 +00:00
259 changed files with 3273 additions and 1361 deletions
View File
+1
View File
@@ -0,0 +1 @@
See the NEWS file.
View File
@@ -18,12 +18,18 @@ 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.
Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt
for details.
This product includes the HZ library (or portions of it). See LICENSE_hz.txt
for details.
This product includes icons from Crystal Project,
copyright 2006-2007 Everaldo Coelho.
http://www.everaldo.com/crystal, http://www.yellowicon.com .
@@ -4,21 +4,33 @@ HZ Library License
The HZ library contains software licensed under several different licenses.
This is due to HZ including portions of other software.
All original code is licensed under the Zlib License. See LICENSE_zlib.txt for
details.
Portions of this software are copyright Regents of the University of California,
see LICENSE_bsd-ucb.txt for details.
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.
Portions of this software are copyright Regents of the University of
California, see LICENSE_bsd-ucb.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.
Each source file contains its copyright information.
If you intend to use HZ in your product, please note that you MUST include
these licenses somewhere in your distribution (simply redistributing the
license files or reproducing their texts in documentation and/or other supplied
materials is OK):
license files or reproducing their texts in documentation and/or other
supplied materials is OK):
* LICENSE_zlib.txt (source distributions only);
* LICENSE_bsd-ucb.txt (source and binary distributions);
* LICENSE_boost_1_0.txt (source distributions only).
If you use only portions of HZ, you may ignore this requirement for files you
don't use. For example, if you don't use any of the Boost-licensed files, you
are not required to distribute the Boost license.
+12
View File
@@ -0,0 +1,12 @@
Whatever License
THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
WARRANTY. IN NO EVENT WILL THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES
ARISING FROM THE USE OF THIS SOFTWARE.
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
@@ -22,6 +22,7 @@ dist_doc_DATA = AUTHORS.txt ChangeLog NEWS README.txt \
LICENSE_gsmartcontrol.txt \
LICENSE_hz.txt \
LICENSE_pcrecpp.txt \
LICENSE_whatever.txt \
LICENSE_zlib.txt
+13
View File
@@ -1,4 +1,17 @@
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).
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.
Improved .desktop files.
License for tests and examples is legally more correct now.
Minor bugfixes.
Version 0.8.0rc3, released on 2008-10-08
Added support for udevless Linux distributions by providing a fallback
/proc/partitions-based drive detection method. The new method adds
+63 -29
View File
@@ -35,6 +35,8 @@ Perform SMART self-tests.
Ability to load smartctl output as a "virtual" device, which acts just like a
real (read-only) device.
Works on most smartctl-supported operating systems.
Extensive help information.
@@ -86,8 +88,6 @@ these bugs and works around them.
Software Requirements
So far, only Linux systems are supported.
You need to have the following software installed:
* pcre - http://www.pcre.org .
@@ -110,6 +110,33 @@ 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:
* Linux - All the popular configurations should work.
* FreeBSD - Tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86.
* NetBSD - Tested with NetBSD 4.0.1 / x86.
* OpenBSD - Tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5.
* 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.
* 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
@@ -120,9 +147,9 @@ 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. One option is to try OpenSUSE Build Service - it
provides ready-to-install packages for various popular Linux distributions
(OpenSUSE, Fedora, etc...). See
installation procedure. For Linux, one option is to try the OpenSUSE Build
Service - it provides ready-to-install packages for various popular Linux
distributions (OpenSUSE, Fedora, etc...). See
http://download.opensuse.org/repositories/home:/alex_sh/ .
If you want to compile from source, check that you have all the required
@@ -130,15 +157,16 @@ dependencies (see Software Requirements section). Then the usual
./configure; make; make install
will build and install it. Installation usually requires root privileges, but
you don't need to install the program in order to run it directly from the
compilation directory.
will build and install it. Installation usually requires adminstrative
privileges, but you don't need to install the program in order to run it
directly from the compilation directory.
Permission Problems
Short answer: you need to be root. In X11, use kdesu, gnomesu, sux or similar.
Short answer: you need to be root (Administrator in Windows).
In X11, use kdesu, gnomesu, sux, xdg-su or similar.
Long answer: read below.
@@ -146,29 +174,25 @@ Most operating systems prohibit direct access to hardware to users with
non-administrative privileges. Unfortunately, to access SMART data, smartctl
needs to directly access the hard drive.
The provided desktop and menu icons should show the "Please enter the root
The provided X11 desktop and menu icons should show the "Please enter the root
password" dialog boxes, and, after correct information is entered, should run
this program with root privileges. The dialogs should be available in most
commonly used X11 desktop environments.
However, in case the described method is somehow inaccessible, you need to
manually invoke the program with kdesu, gnomesu, sux or similar programs. For
example,
Another way is to use the included gsmartcontrol_root.sh script, which finds
the available su program and runs gsmartcontrol with it.
Yet another way is to manually invoke the program with kdesu, gnomesu, sux or
similar programs. For example,
kdesu -u root -c gsmartcontrol
will ask for root password and run gsmartcontrol with root privileges. Replace
"kdesu" with "gnomesu" if using Gnome. The "sux" command may also help, if
neither KDE or GNOME are available.
"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also
help, if neither KDE or GNOME are available.
Another method would be to set the "setuid" flag on smartctl binary, for
example (run as root):
chmod 4755 `which smartctl`
This method is not recommended, however, and is considered a security risk.
Also, some systems may reset the change automatically, unless explicitly
instructed otherwise.
Please don't set the "setuid" flag on smartctl binary. It is considered a
security risk.
@@ -210,7 +234,7 @@ 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.
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
@@ -234,20 +258,30 @@ language and all.
Reporting Bugs
First, please collect the following information about your system. Without it
it may be very hard or impossible to fix the bug.
If it is a SMART or drive-related problem, please try to test it with smartctl
first. Chances are, the problem you're experiencing is not tied to
GSmartControl, but is a drive firmware or smartctl problem. For example, to
see a complete information about your /dev/sda drive, type the following in a
terminal emulator (e.g., xterm, konsole or gnome-terminal):
smartctl -a /dev/sda
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).
* What version of GTK and Gtkmm you have installed. Finding this out is very
* Which version of GTK and Gtkmm you have installed. Finding this out is very
distribution-specific. For example, on OpenSUSE it would be
"rpm -q gtk2 gtkmm2". Some distributions have gtkmm24 instead. You may also
search them in your distribution's graphical package manager, if there is one.
* Execution log from the program, if possible. To obtain it, run the program
with -v option, e.g. (type the following in terminal emulator or Run dialog):
with -v option, e.g. (type the following in a terminal emulator or Run
dialog):
kdesu -- biocalcon -v
gsmartcontrol_root.sh other -v
Perform the steps needed to reproduce the bug, then go to
"Options -> View Execution Log", and click "Save All".
+15 -28
View File
@@ -19,6 +19,14 @@ A treeview (also iconview, combobox) from glade comes
with existing model, which should be unset
--------------------------------------------------------
portability:
win32 32-bit: int: 4, long: 4, pointer: 4.
win32 64-bit: int: 4, long: 8, pointer: 8.
sparc: same as win32 (what about OS?).
--------------------------------------------------------
GTKMM problems:
@@ -34,43 +42,22 @@ no ComboBox::unset_model() and IconView::unset_model().
--------------------------------------------------------
TODO:
if -q is passed, Execution log doesn't contain libdebug log. fix.
Drive detection - any cross-platform way to do it?
FreeBSD: /dev/ad[0-3], partitions - /dev/ad[]
http://www.freebsd.org/doc/en/books/handbook/disks-naming.html
Non-existent devices give ENXIO (6, Device not configured).
In Linux ENXIO has name (6, No such device or address).
/dev/ad0s1b none swap sw 0 0
/dev/ad0s1a / ufs rw 1 1
/dev/acd0c /cdrom cd9660 ro,noauto 0 0
Do NOT open cd devices - it may cause hang on music cd, etc... .
In the slice case the drive will be added as /dev/da1s1e. This is read as:
SCSI disk, unit number 1 (second SCSI disk), slice 1 (PC BIOS partition 1),
and e BSD partition. In the dedicated case, the drive will be added simply as /dev/da1e.
iphone:
/dev/disk0
/dev/disk0s1, /dev/disk0s2
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).
desktop files: gnomesu / gtksu wrapper.
desktop_other - find suitable su.
----------- Feature List ----------
+Autodetect available devices (through /proc / /sys ?).
win32 support for autodetection (what about different smartctl drivers?).
+Autodetect available devices
+Select current device (from autodetected list; manually whitelist devices).
@@ -117,7 +104,7 @@ SCT Temperatures (do we use these?)
This configures temperature logging interval (1 min by default) and clears the history.
Tests:
+Tests:
Ability to detect a testing drive on boot, show its info with testing tab.
Run one-time Immediate Offline test (-t offline).
+34
View File
@@ -0,0 +1,34 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_AUTO_CLEAR_FLAGS()
# Clear user-specified CFLAGS / CXXFLAGS / LIBS / LDFLAGS if requested.
# You should call this macro before any of AC_PROG_C(XX) checks to
# avoid checking with user-specified options.
AC_DEFUN([APP_AUTO_CLEAR_FLAGS], [
AC_ARG_ENABLE(user-flags, AS_HELP_STRING([--disable-user-flags],
[ignore user-supplied compiler flags (default: user flags are enabled)]),
[app_cv_compiler_user_flags=${enableval}], [app_cv_compiler_user_flags=yes])
AC_MSG_NOTICE([Enable user-specified compiler flags: $app_cv_compiler_user_flags])
# Reset user-supplied flags if requested.
if test "x$app_cv_compiler_user_flags" = "xno"; then
CFLAGS="";
CXXFLAGS="";
LIBS="";
LDFLAGS="";
fi
])
+134
View File
@@ -0,0 +1,134 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_CHOOSE_GLD_READER([C|C++][, flags_prefix])
# This macro detects whether GtkBuilder (from gtk >= 2.12) or libglade
# are present. If the first parameter is C++, then gtkmm and libglademm
# are searched for instead.
# flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLIBS
# will be initialized (or appended to) with the correct flags in case of libglade(mm).
# app_cv_gld_reader will be set to none, libglade or gtkbuilder.
# ENABLE_LIBGLADE or ENABLE_GTKBUILDER will be exported to config.h
# You must call AX_COMPILER_VENDOR before using this macro.
AC_DEFUN([APP_CHOOSE_GLD_READER], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
# This is just in case
# PKG_PROG_PKG_CONFIG()
# this variable will have values "none", "libglade" or "gtkbuilder".
app_cv_gld_reader="none";
# in case libglade(mm) is found, these will contain the flags.
app_cv_gld_reader_CFLAGS="";
app_cv_gld_reader_CXXFLAGS="";
app_cv_gld_reader_LIBS="";
app_cv_gld_reader_libglade_name="libglade";
app_cv_gld_reader_libglade_pkg_name="libglade-2.0";
app_cv_gld_reader_gtk_name="gtk";
app_cv_gld_reader_gtk_pkg_name="gtk+-2.0";
if test "x$1" = "xC++"; then
app_cv_gld_reader_libglade_name="libglademm";
app_cv_gld_reader_libglade_pkg_name="libglademm-2.4";
app_cv_gld_reader_gtk_name="gtkmm";
app_cv_gld_reader_gtk_pkg_name="gtkmm-2.4";
fi
# Manual control. AC_ARG_ENABLE doesn't get bash variables, so we have
# to do it this way. If put inside a shell conditional, it still shows up in configure help.
AC_ARG_ENABLE(libglade, AS_HELP_STRING([--enable-libglade],
[use libglade(mm) instead of GtkBuilder (default: auto)]),
[with_libglade=${enableval}], [with_libglade=auto])
# Try gtkbuilder first
if test "x$with_libglade" = "xno" || test "x$with_libglade" = "xauto"; then
PKG_CHECK_EXISTS([$app_cv_gld_reader_gtk_pkg_name >= 2.12.0],
[app_cv_gld_reader="gtkbuilder"])
fi
if test "$with_libglade" = "no" && test "$app_cv_gld_reader" = "none"; then
AC_MSG_ERROR([GtkBuilder support not found (you need $app_cv_gld_reader_gtk_name version 2.12 or higher). Try using $app_cv_gld_reader_libglade_name instead.])
fi
# libglade specified, or auto and no gtkbuilder. gtkmm2.4-compatible libglademm is >= 2.4.0.
# (not sure about libglade version).
if test "x$app_cv_gld_reader" = "xnone"; then
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])])
fi
if test "x$with_libglade" = "xyes" && test "x$app_cv_gld_reader" = "xnone"; then
AC_MSG_ERROR([$app_cv_gld_reader_libglade_name not found. Try building with GtkBuilder support (you need $app_cv_gld_reader_gtk_name version 2.12 or higher).])
fi
# still not found.
if test "$app_cv_gld_reader" = "none"; then
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="";
fi
# export the flags
if test "x$2" != "x"; then
if test "x$$2[]CFLAGS" = "x"; then
$2[]CFLAGS="$app_cv_gld_reader_CFLAGS";
else
$2[]CFLAGS="$$2[]CFLAGS $app_cv_gld_reader_CFLAGS";
fi
if test "x$$2[]CXXFLAGS" = "x"; then
$2[]CXXFLAGS="$app_cv_gld_reader_CXXFLAGS";
else
$2[]CXXFLAGS="$$2[]CXXFLAGS $app_cv_gld_reader_CXXFLAGS";
fi
if test "x$$2[]LIBS" = "x"; then
$2[]LIBS="$app_cv_gld_reader_LIBS";
else
$2[]LIBS="$$2[]LIBS $app_cv_gld_reader_LIBS";
fi
fi
# export to automake.am-s
AM_CONDITIONAL([ENABLE_LIBGLADE], [test "$app_cv_gld_reader" = "libglade"])
AM_CONDITIONAL([ENABLE_GTKBUILDER], [test "$app_cv_gld_reader" = "gtkbuilder"])
# export to config.h
if test "$app_cv_gld_reader" = "libglade"; then
AC_DEFINE(ENABLE_LIBGLADE, 1, [Use libglade(mm) instead of GtkBuilder])
fi
if test "$app_cv_gld_reader" = "gtkbuilder"; then
AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm])
fi
# If we move this to the start, some messages get in between, so we have to have it here.
AC_MSG_CHECKING([for glade file support:])
AC_MSG_RESULT([$app_cv_gld_reader])
])
+246
View File
@@ -0,0 +1,246 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_COMPILER_OPTIONS([flags_prefix])
# This macro enables various compiler flags (common warnings, optimization,
# debug, etc...), controllable through configure options.
# flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLDFLAGS
# will be initialized (or appended to) with the results.
# DEBUG and DEBUG_BUILD will be exported to config.h in case of debug builds.
# You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...)
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
AC_DEFUN([APP_COMPILER_OPTIONS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_ENV])
app_cv_compiler_options_cflags="";
app_cv_compiler_options_cxxflags="";
app_cv_compiler_options_ldflags="";
# ---- Common compiler options (warnings, etc...)
AC_ARG_ENABLE(common-options, AS_HELP_STRING([--enable-common-options=<compiler>|auto|none],
[enable useful compilation options (warnings, mostly). Accepted values are auto, none, gnu, intel, sun. (Default: auto)]),
[app_cv_compiler_common_options=${enableval}], [app_cv_compiler_common_options=auto])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_common_options" = "xyes" || test "x$app_cv_compiler_common_options" = "xauto"; then
app_cv_compiler_common_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_common_options" = "xno"; then
# for pretty message
app_cv_compiler_common_options="none";
fi
AC_MSG_NOTICE([Enable common compiler flags for: $app_cv_compiler_common_options])
if test "x$app_cv_compiler_common_options" != "xnone"; then
# 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
# 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"
fi
# Note: Disabled -Wconversion, it was causing lots of silly warnings under x86-64.
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual \
-Wctor-dtor-privacy -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
-Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings";
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags $app_cv_compiler_tmp_var";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags $app_cv_compiler_tmp_var";
# intel / gnu environment
elif test "x$app_cv_compiler_common_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -w1";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \
-ftrapuv -fstack-security-check -early-template-check -static-libgcc -static-intel \
-Wcomment -Wdeprecated -Wextra-tokens -Wformat -Wmissing-prototypes -Wnon-virtual-dtor \
-Wpointer-arith -Wreturn-type -Wshadow -Wtrigraphs -Wuninitialized -w1";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -static-libgcc -static-intel";
# 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
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 \
+w -errtags -erroff=notemsource,notused -features=extensions";
fi
fi
# ---- Compiler options for debug builds
AC_ARG_ENABLE(debug-options, AS_HELP_STRING([--enable-debug-options=<compiler>|auto|none],
[enable debug build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_debug_options=${enableval}], [app_cv_compiler_debug_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_debug_options" = "xyes" || test "x$app_cv_compiler_debug_options" = "xauto"; then
app_cv_compiler_debug_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_debug_options" = "xno"; then
# for pretty message
app_cv_compiler_debug_options="none";
fi
AC_MSG_NOTICE([Enable debug build flags for: $app_cv_compiler_debug_options])
if test "x$app_cv_compiler_debug_options" != "xnone"; then
# Define DEBUG and DEBUG_BUILD for debug builds (through config.h).
AC_DEFINE([DEBUG], [1], [Defined for debug builds])
AC_DEFINE([DEBUG_BUILD], [1], [Defined for debug builds])
# gcc, mingw
if test "x$app_cv_compiler_debug_options" = "xgnu"; then
# We could put libstdc++ debug options here, but it generates binary-incompatible
# C++ code, which leads to runtime errors with e.g. libsigc++.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0";
# intel / gnu
elif test "x$app_cv_compiler_debug_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0";
# suncc
elif test "x$app_cv_compiler_debug_options" = "xsun"; then
# There's no -O0, it's the default. Debug format is stabs by default, but can be switched
# to dwarf by -xdebugformat=dwarf.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xs -g";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xs -g";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -xs -g";
fi
fi
# ---- Compiler options for optimized builds
AC_ARG_ENABLE(optimize-options, AS_HELP_STRING([--enable-optimize-options=<compiler>|auto|none],
[enable optimized build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_optimize_options=${enableval}], [app_cv_compiler_optimize_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_optimize_options" = "xyes" || test "x$app_cv_compiler_optimize_options" = "xauto"; then
app_cv_compiler_optimize_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_optimize_options" = "xno"; then
# for pretty message
app_cv_compiler_optimize_options="none";
fi
AC_MSG_NOTICE([Enable optimized build flags for: $app_cv_compiler_optimize_options])
if test "x$app_cv_compiler_optimize_options" != "xnone"; then
# 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
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -mtune=generic";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -mtune=generic";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -mtune=generic";
fi
# intel / gnu
elif test "x$app_cv_compiler_optimize_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s";
# suncc
elif test "x$app_cv_compiler_optimize_options" = "xsun"; then
# There's no -O0, it's the default. Debug format is stabs by default, but can be switched
# to dwarf by -xdebugformat=dwarf.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xO5";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xO5";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -s";
fi
fi
# ---- Compiler options for GCC pch support
AC_ARG_ENABLE(gcc-pch,
AS_HELP_STRING([--enable-gcc-pch],[enable precompiled header support (pch make target) (GCC only) (default: disabled)
(use "make gch" to speed up the compilation, but don't enable it if you don't intend to use it - it will slow things down)]),
[app_cv_compiler_gcc_pch=${enableval}], [app_cv_compiler_gcc_pch=no])
AC_MSG_NOTICE([Enable GCC precompiled header usage: $app_cv_compiler_gcc_pch])
# Don't check the vendor here - it's disabled by default, so it's on the user.
if test "x$app_cv_compiler_gcc_pch" = "xyes"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -Winvalid-pch -DENABLE_PCH"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -Winvalid-pch -DENABLE_PCH"
fi
# ---- Export our variables
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_compiler_options_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_compiler_options_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_compiler_options_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_compiler_options_cxxflags";
fi
if test "x$$1[]LDFLAGS" = "x"; then
$1[]LDFLAGS="$app_cv_compiler_options_ldflags";
else
$1[]LDFLAGS="$$1[]LDFLAGS $app_cv_compiler_options_ldflags";
fi
AC_MSG_NOTICE([Compiler-specific build options:])
AC_MSG_NOTICE([CFLAGS: $app_cv_compiler_options_cflags])
AC_MSG_NOTICE([CXXFLAGS: $app_cv_compiler_options_cxxflags])
AC_MSG_NOTICE([LDFLAGS: $app_cv_compiler_options_ldflags])
])
+46
View File
@@ -0,0 +1,46 @@
############################################################################
# Copyright:
# (C) 2008 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
# Intel treat them equally, so no overloads are needed (or accepted)
# there.
# SunCC accepts C++-to-C function pointer assignment in function
# parameters, but not in constructor parameters. To make such
# code work an additional C-function-pointer constructor must be
# specified. Then the constructor itself can make the conversion
# (with a compiler warning though).
# SunCC also disallows C-to-C++ function pointer specification
# for template parameters (I don't remember the exact details).
# If such support is found, HAVE_CXX_EXTERN_C_OVERLOAD is defined.
AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [
AC_CACHE_CHECK(whether $CXX accepts extern C function pointer overload, ac_cv_cxx_extern_c_overload, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
extern "C" { typedef void (*c_func_t)(void); }
typedef void (*cpp_func_t)(void);
void f1(c_func_t f) { }
void f1(cpp_func_t f) { }
]], [[]])],
[ac_cv_cxx_extern_c_overload=yes], [ac_cv_cxx_extern_c_overload=no])
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])
fi
])
+44
View File
@@ -0,0 +1,44 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports C99 __func__.
AC_DEFUN([APP_CXX___FUNC__], [
AC_CACHE_CHECK(whether $CXX recognizes __func__, app_cv_cxx___func__, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
[[const char* s = __func__;]])],
[app_cv_cxx___func__=yes], [app_cv_cxx___func__=no])
AC_LANG_POP([])
])
if test "x$app_cv_cxx___func__" = "xyes"; then
AC_DEFINE(HAVE_CXX___func__, ,
[Define if the C++ complier supports __func__])
fi
])
# Check whether C++ compiler supports __FUNCTION__.
AC_DEFUN([APP_CXX___FUNCTION__], [
AC_CACHE_CHECK(whether $CXX recognizes __FUNCTION__, app_cv_cxx___FUNCTION__, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
[[const char* s = __FUNCTION__;]])],
[app_cv_cxx___FUNCTION__=yes], [app_cv_cxx___FUNCTION__=no])
AC_LANG_POP([])
])
if test "x$app_cv_cxx___FUNCTION__" = "xyes"; then
AC_DEFINE(HAVE_CXX___FUNCTION__, ,
[Define if the C++ complier supports __FUNCTION__])
fi
])
+323
View File
@@ -0,0 +1,323 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_DETECT_OS_KERNEL(triplet_name, define_macro_prefix)
# Check target OS kernel.
# triplet_name is target, host or build.
# You must call AX_COMPILER_VENDOR before using this macro.
# If you call
# APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL])
# you will get CONFIG_KERNEL_LINUX C macro exported to config.h
# (on gcc/linux).
# If no define_macro_prefix is specified, no C macro prefix will be used.
# app_cv_target_os_kernel will be set to kernel name (lowercase).
# app_cv_target_os_kernel_macro will be set to kernel name (all caps).
# TARGET_OS_KERNEL_LINUX automake conditional will be set to true.
# Note: Some operating systems / compilers have built-in defines.
# For gcc, check with
# $ gcc -dM -E - < /dev/null
AC_DEFUN([APP_DETECT_OS_KERNEL], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_MSG_CHECKING([for $1 OS kernel])
app_cv_[]$1[]_os_kernel="";
app_cv_[]$1[]_os_kernel_macro="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
# "build" - the type of system on which the package is being configured and compiled.
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[Triplet name must be build, host or target.]])
fi
case "$$1" in
*linux*)
# gcc defines __linux__ on linux. Other defines include: __linux, __unix__, __gnu_linux__,
# linux, unix, __i386__, i386.
app_cv_[]$1[]_os_kernel="linux";
app_cv_[]$1[]_os_kernel_macro="LINUX";
;;
*mingw* | *cygwin*)
# mingw defines _WIN32. msvc defines _WIN32 (built-in), and WIN32 via windows.h.
# Other mingw defines include: __WINNT, __WINNT__, __WIN32__, __i386, _X86_,
# i386, __i386__, WIN32, __MINGW32__, WINNT (all equal to 1).
app_cv_[]$1[]_os_kernel="windows";
app_cv_[]$1[]_os_kernel_macro="WINDOWS";
;;
# This includes debian gnu/kfreebsd. Dragonfly is checked separately.
*freebsd*)
# gcc built-in defines (to check for freebsd kernel):
# defined(__FreeBSD__) || defined(__FreeBSD_kernel__).
# See http://glibc-bsd.alioth.debian.org/porting/PORTING .
app_cv_[]$1[]_os_kernel="freebsd";
app_cv_[]$1[]_os_kernel_macro="FREEBSD";
;;
*dragonfly*)
app_cv_[]$1[]_os_kernel="dragonfly";
app_cv_[]$1[]_os_kernel_macro="DRAGONFLY";
;;
*openbsd*)
app_cv_[]$1[]_os_kernel="openbsd";
app_cv_[]$1[]_os_kernel_macro="OPENBSD";
;;
# This includes debian gnu/netbsd.
*netbsd*)
app_cv_[]$1[]_os_kernel="netbsd";
app_cv_[]$1[]_os_kernel_macro="NETBSD";
;;
*solaris*)
app_cv_[]$1[]_os_kernel="solaris";
app_cv_[]$1[]_os_kernel_macro="SOLARIS";
;;
*darwin*)
app_cv_[]$1[]_os_kernel="darwin";
app_cv_[]$1[]_os_kernel_macro="DARWIN";
;;
*qnx*)
app_cv_[]$1[]_os_kernel="qnx";
app_cv_[]$1[]_os_kernel_macro="QNX";
;;
*)
app_cv_[]$1[]_os_kernel="unknown";
app_cv_[]$1[]_os_kernel_macro="UNKNOWN";
;;
esac
# Assign this - we may use this in configure itself.
if test "x$2" != "x"; then
app_cv_[]$1[]_os_kernel_macro="$2[]_${app_cv_[]$1[]_os_kernel_macro}";
fi
# config.h macros (yes, we need this many. otherwise config.h will be incorrect).
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])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"; then
AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"; then
AC_DEFINE($2[]OPENBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"; then
AC_DEFINE($2[]NETBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"; then
AC_DEFINE($2[]SOLARIS, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"; then
AC_DEFINE($2[]DARWIN, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"; then
AC_DEFINE($2[]QNX, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"; then
AC_DEFINE($2[]UNKNOWN, 1, [$1 OS kernel])
fi
# 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_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"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_NETBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_SOLARIS, [test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DARWIN, [test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"])
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"])
AC_MSG_RESULT([${app_cv_[]$1[]_os_kernel}])
])
# APP_DETECT_OS_ENV(triplet_name, define_macro_prefix)
# Check target OS userland environment (mainly glibc).
# triplet_name is target, host or build.
# You must call AX_COMPILER_VENDOR before using this macro.
# If you call
# APP_DETECT_OS_ENV([target], [COMPILER], [CONFIG_OS_ENV])
# you will get CONFIG_OS_ENV_LINUX C macro exported to config.h
# (on gcc/linux).
# If no define_macro_prefix is specified, no C macro prefix will be used.
# app_cv_target_os_env will be set to env name (lowercase).
# app_cv_target_os_env_macro will be set to env name (all caps).
# TARGET_OS_ENV_LINUX automake conditional will be set to true.
AC_DEFUN([APP_DETECT_OS_ENV], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_MSG_CHECKING([for $1 OS userland environment])
app_cv_[]$1[]_os_env="";
app_cv_[]$1[]_os_env_macro="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
# "build" - the type of system on which the package is being configured and compiled.
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[Triplet name must be build, host or target.]])
fi
case "$$1" in
*cygwin*)
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";
;;
# This includes gnu/linux, debian gnu/kfreebsd, debian gnu/netbsd...
*-gnu | gnu*)
app_cv_[]$1[]_os_env="gnu";
app_cv_[]$1[]_os_env_macro="GNU";
;;
# Freebsd libc
*freebsd*)
app_cv_[]$1[]_os_env="freebsd";
app_cv_[]$1[]_os_env_macro="FREEBSD";
;;
*dragonfly*)
app_cv_[]$1[]_os_env="dragonfly";
app_cv_[]$1[]_os_env_macro="DRAGONFLY";
;;
*openbsd*)
app_cv_[]$1[]_os_env="openbsd";
app_cv_[]$1[]_os_env_macro="OPENBSD";
;;
*netbsd*)
app_cv_[]$1[]_os_env="netbsd";
app_cv_[]$1[]_os_env_macro="NETBSD";
;;
*solaris*)
app_cv_[]$1[]_os_env="solaris";
app_cv_[]$1[]_os_env_macro="SOLARIS";
;;
*darwin*)
app_cv_[]$1[]_os_env="darwin";
app_cv_[]$1[]_os_env_macro="DARWIN";
;;
*qnx*)
app_cv_[]$1[]_os_env="qnx";
app_cv_[]$1[]_os_env_macro="QNX";
;;
*)
app_cv_[]$1[]_os_env="unknown";
app_cv_[]$1[]_os_env_macro="UNKNOWN";
;;
esac
# Assign this even if not using flags - we may use this in configure itself.
if test "x$2" != "x"; then
app_cv_[]$1[]_os_env_macro="$2[]_${app_cv_[]$1[]_os_env_macro}";
fi
# config.h macros (yes, we need this many. otherwise config.h will be incorrect).
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])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xgnu"; then
AC_DEFINE($2[]GNU, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"; then
AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"; then
AC_DEFINE($2[]OPENBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"; then
AC_DEFINE($2[]NETBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xsolaris"; then
AC_DEFINE($2[]SOLARIS, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xdarwin"; then
AC_DEFINE($2[]DARWIN, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xqnx"; then
AC_DEFINE($2[]QNX, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xunknown"; then
AC_DEFINE($2[]UNKNOWN, 1, [$1 OS userspace environment])
fi
# 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_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"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_OPENBSD, [test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_NETBSD, [test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_SOLARIS, [test "x${app_cv_[]$1[]_os_env}" = "xsolaris"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DARWIN, [test "x${app_cv_[]$1[]_os_env}" = "xdarwin"])
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}])
])
+171
View File
@@ -0,0 +1,171 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_GET_MT_FLAGS(flags_prefix[, action-if-not-found])
# This macro detects compiler flags which are needed to compile a multi-threaded
# (possibly pthreads-based) applications on target OS. flags_prefixCFLAGS,
# flags_prefixCXXFLAGS and flags_prefixLIBS will be initialized (or appended to)
# with the correct flags.
# You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...)
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
AC_DEFUN([APP_GET_MT_FLAGS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_KERNEL])
AC_REQUIRE([APP_DETECT_OS_ENV])
AC_MSG_CHECKING([for target thread support flags])
app_cv_target_thread_cflags="";
app_cv_target_thread_cxxflags="";
app_cv_target_thread_libs="";
app_cv_target_thread_found="no";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
if test "x${app_cv_target_os_kernel}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_kernel] not set.])
fi
if test "x${app_cv_target_os_env}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_env] not set.])
fi
# Compilers use at least 3 defines to indicate thread-safe support to standard libs:
# _THREAD_SAFE, _MT, _REENTRANT.
# We use the ones that are required for detected platform.
case "$app_cv_target_os_kernel" in
linux)
# glibc manual says that defining either _REENTRANT or _THREAD_SAFE
# enables some thread-safe implementations of glibc functions.
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel" \
|| test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# suncc needs -lpthread on posix but not solaris.
# -mt defines _REENTRANT too. Don't use -lthread.
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt -lpthread"
app_cv_target_thread_found="yes";
fi
;;
windows)
# NOTE: Not sure about cygwin.
# NOTE: This only enables multithreaded code generation. For linking
# with pthreads-win32 you need additional flags.
# gcc man page: -mthreads: Support thread-safe exception handling on Mingw32.
# -mthreads defines -D_MT, and links with -lmingwthrd.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_libs="-mthreads"
app_cv_target_thread_found="yes"
fi
;;
freebsd | dragonfly | netbsd | openbsd)
# NOTE: Not sure about debian gnu/kfreebsd and gnu/netbsd.
# Freebsd had -kthread, but it has been discontinued (afaik).
# For freebsd -pthread already replaces libc with libc_r.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
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"
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.
# 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"
app_cv_target_thread_found="yes"
fi
;;
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
;;
qnx)
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# afaik, gcc/qnx produces mt code by default (at least on newer qnx releases),
# so no flags are needed.
app_cv_target_thread_found="yes"
fi
;;
esac
if test "$app_cv_target_thread_found" = "no"; then
AC_MSG_WARN([Cannot detect compiler thread support. Set CFLAGS, CXXFLAGS and LIBS manually.])
m4_ifvaln([$2],[$2])dnl
else
AC_MSG_RESULT([CFLAGS: $app_cv_target_thread_cflags; CXXFLAGS: $app_cv_target_thread_cxxflags; LIBS: $app_cv_target_thread_libs])
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_target_thread_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_target_thread_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_target_thread_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_thread_cxxflags";
fi
if test "x$$1[]LIBS" = "x"; then
$1[]LIBS="$app_cv_target_thread_libs";
else
$1[]LIBS="$$1[]LIBS $app_cv_target_thread_libs";
fi
fi
])
+101
View File
@@ -0,0 +1,101 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_USE_SYSTEM_EXTENSIONS([flags_prefix])
# This macro detects compiler flags which are needed to enable
# system-specific library extensions. For example, on GNU platforms
# it's -D_GNU_SOURCE, on solaris it's -D__EXTENSIONS__, etc...
# Basically, it's the same as AC_USE_SYSTEM_EXTENSIONS,
# but uses compiler flag approach instead of config.h.
# You must call AX_COMPILER_VENDOR
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
# More info:
# http://www.gnu.org/software/autoconf/manual/autoconf.html#Posix-Variants
# http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
# http://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.predefined
# http://docs.sun.com/app/docs/doc/817-3946/6mjgmt4o8?l=en&a=view
AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_ENV])
AC_MSG_CHECKING([for target system extension flags])
app_cv_target_extension_cflags="";
app_cv_target_extension_cxxflags="";
app_cv_target_extension_libs="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
if test "x${app_cv_target_os_kernel}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_kernel] not set.])
fi
if test "x${app_cv_target_os_env}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_env] not set.])
fi
case "$app_cv_target_os_env" in
gnu)
# glibc all-extensions macro: _GNU_SOURCE.
# gcc automatically defines it in later versions.
# suncc needs the flag explicitly.
# http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
app_cv_target_extension_cflags="-D_GNU_SOURCE"
app_cv_target_extension_cxxflags="-D_GNU_SOURCE"
;;
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.
# To support some posix macros, additional link objects are also required.
# http://docs.sun.com/app/docs/doc/816-5175/standards-5?a=view
;;
esac
AC_MSG_RESULT([CFLAGS: $app_cv_target_extension_cflags; CXXFLAGS: $app_cv_target_extension_cxxflags; LIBS: $app_cv_target_extension_libs])
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_target_extension_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_target_extension_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_target_extension_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_extension_cxxflags";
fi
if test "x$$1[]LIBS" = "x"; then
$1[]LIBS="$app_cv_target_extension_libs";
else
$1[]LIBS="$$1[]LIBS $app_cv_target_extension_libs";
fi
])
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
stamp="../gsmartcontrol-`date +%F`"
echo $stamp
+117 -278
View File
@@ -1,13 +1,16 @@
AC_INIT([GSmartControl], [0.8.0rc3], [], [gsmartcontrol])
AC_INIT([GSmartControl], [0.8.0rc4], [], [gsmartcontrol])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_TARGET
# disable portability warnings because of pattern rules in src/res.
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 -Wall,no-portability])
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 -Wall])
# Clear *FLAGS, LIBS, LDFLAGS if requested through configure option.
APP_AUTO_CLEAR_FLAGS()
# C compiler, C preprocessor
AC_PROG_CC
@@ -33,37 +36,36 @@ AC_CHECK_TOOL(RANLIB, ranlib)
# -------------------------------------------------------------------------------------
# Various compiler features
# ------------- Detect compiler, OS, environment. Enable system features.
# detect compiler vendor (sets $ax_cv_cxx_compiler_vendor)
AX_COMPILER_VENDOR
# detect RTTI (defines HAVE_RTTI)
AC_CXX_RTTI
# Detect target OS kernel and userspace and export to config.h
# (also define automake conditionals).
APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL_])
APP_DETECT_OS_ENV([target], [CONFIG_OS_ENV_])
# detect exception support (defines HAVE_EXCEPTIONS)
AC_CXX_EXCEPTIONS
# Detect common (warning, debug, optimize) compiler flags and
# export them to C(XX)FLAGS, LDFLAGS.
# We don't use prefix because this way the flags will be used for tests,
# which is important for some compatibility flags (e.g. __func__ visibility, etc...).
APP_COMPILER_OPTIONS([])
# detect abi::__cxa_demangle (defines HAVE_GCC_ABI_DEMANGLE)
AX_CXX_GCC_ABI_DEMANGLE
# Thread support for known configurations.
APP_GET_MT_FLAGS([])
# detect __gnu_cxx::__verbose_terminate_handler (defines HAVE_VERBOSE_TERMINATE_HANDLER.)
AC_CXX_VERBOSE_TERMINATE_HANDLER
# Enable _GNU_SOURCE, etc...
APP_USE_SYSTEM_EXTENSIONS([])
# detect __func__ support (defines HAVE_CXX__FUNC)
AC_CXX_VAR_FUNC
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Portability stuff
# arrange large file support (define _FILE_OFFSET_BITS 64, etc...)
# Arrange large file support (define _FILE_OFFSET_BITS 64, etc...)
AC_SYS_LARGEFILE
# ------------- Correct incomplete standard libraries
# define int8_t and friends if the standard doesn't have them
AC_TYPE_INT8_T
AC_TYPE_UINT8_T
@@ -79,6 +81,33 @@ AC_TYPE_UINTMAX_T
AC_TYPE_LONG_LONG_INT
AC_TYPE_UNSIGNED_LONG_LONG_INT
# ------------- Language / Standard Library checks
# detect RTTI (defines HAVE_RTTI)
AC_CXX_RTTI
# detect exception support (defines HAVE_EXCEPTIONS)
AC_CXX_EXCEPTIONS
# detect abi::__cxa_demangle (defines HAVE_GCC_ABI_DEMANGLE)
AX_CXX_GCC_ABI_DEMANGLE
# detect __gnu_cxx::__verbose_terminate_handler (defines HAVE_VERBOSE_TERMINATE_HANDLER.)
AC_CXX_VERBOSE_TERMINATE_HANDLER
# detect __func__ support (defines HAVE_CXX___func__)
APP_CXX___FUNC__
# detect __FUNCTION__ support (defines HAVE_CXX___FUNCTION__)
APP_CXX___FUNCTION__
# detect if the compiler accepts extern "C" function pointer overloads
# (defines HAVE_CXX_EXTERN_C_OVERLOAD)
APP_CXX_EXTERN_C_OVERLOAD
# This may be needed for pch support
AC_CHECK_HEADERS([stdc++.h])
@@ -87,9 +116,9 @@ AC_CHECK_FUNCS([strtoll _strtoi64], [break])
# Non-C99 systems don't have strtof and strtold. some have strtodf for strtof,
# but let's ignore it. Note: They may be supported by system libc,
# but unsupported by headers (e.g. sun studio on linux). In that case,
# 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 / link check:
# Header check is the best way to determine availability:
AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# link check only:
# AC_CHECK_FUNCS([strtof strtold])
@@ -100,39 +129,10 @@ AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# -------------------------------------------------------------------------------------
# "build" - the type of system on which the package is being configured and compiled.
ac_windows_build="no"
ac_linux_build="no"
case "${host}" in
*mingw*)
ac_windows_build="yes"
;;
*linux*)
ac_linux_build="yes"
;;
esac
# Non-standard feature checks
# export to Makefile.am-s
AM_CONDITIONAL([WINDOWS_BUILD], [test "$ac_windows_build" = "yes"])
AM_CONDITIONAL([LINUX_BUILD], [test "$ac_linux_build" = "yes"])
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
ac_windows_target="no"
ac_linux_target="no"
case "${target}" in
*mingw*)
ac_windows_target="yes"
;;
*linux*)
ac_linux_target="yes"
;;
esac
# export to Makefile.am-s
AM_CONDITIONAL([WINDOWS_TARGET], [test "$ac_windows_target" = "yes"])
AM_CONDITIONAL([LINUX_TARGET], [test "$ac_linux_target" = "yes"])
# getrawpartition() for netbsd and openbsd in -lutil.
AC_CHECK_LIB(util, getrawpartition, [ LIBS="$LIBS -lutil" ])
# -------------------------------------------------------------------------------------
@@ -140,124 +140,11 @@ AM_CONDITIONAL([LINUX_TARGET], [test "$ac_linux_target" = "yes"])
# -------------------------------------------------------------------------------------
COMPILER_CXXFLAGS=""
COMPILER_LDFLAGS=""
# reset original user-supplied *FLAGS
RESET_ORIGINAL="no"
AC_ARG_ENABLE(default-gcc-options,
AS_HELP_STRING([--enable-default-gcc-options],[enable default compilation options (warnings, mostly) (GCC only) (default: disabled)]),
[default_gcc_options=${enableval}], [default_gcc_options=no])
if test "$default_gcc_options" = "yes"; then
if test "$ac_windows_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -mms-bitfields -mwindows"
COMPILER_LDFLAGS="$COMPILER_CXXFLAGS -mms-bitfields"
fi
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS \
-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"
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc-debug,
AS_HELP_STRING([--enable-gcc-debug],[enable debug build (GCC only) (default: disabled)]),
[gcc_debug=${enableval}], [gcc_debug=no])
if test "$gcc_debug" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g3 -O0 -DDEBUG -DDEBUG_BUILD"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -g3 -O0"
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc-optimize,
AS_HELP_STRING([--enable-gcc-optimize],[enable optimized build (GCC only) (default: disabled)]),
[gcc_optimize=${enableval}], [gcc_optimize=no])
if test "$gcc_optimize" = "yes"; then
# Note: Do NOT put CPU flags here - they make the program non-portable.
if test "$ac_windows_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g0 -O3 -s"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -s"
else
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g0 -O3 -s -mtune=generic"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -s -mtune=generic"
fi
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc_pch,
AS_HELP_STRING([--enable-gcc-pch],[enable precompiled header support (pch make target) (GCC only) (default: disabled)
(use "make gch" to speed up the compilation)]),
[gcc_pch=${enableval}], [gcc_pch=no])
if test "$gcc_pch" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -Winvalid-pch -DENABLE_PCH"
fi
# define LINUX for linux target
if test "$ac_linux_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -DLINUX"
fi
# Reset user-supplied flags if one of the above options is present.
# We append COMPILER_* counterparts in the end, to override any
# previous options.
if test "$RESET_ORIGINAL" = "yes"; then
CFLAGS=""
CXXFLAGS=""
LIBS=""
LDFLAGS=""
fi
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Enable compiler thread switches for known platforms.
AC_MSG_NOTICE([checking thread support flags...])
THREAD_CFLAGS=""
THREAD_LIBS=""
THREAD_FOUND="no"
if test "$ac_windows_target" = "yes"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
THREAD_CFLAGS="-mthreads -D_THREAD_SAFE"
THREAD_LIBS="-mthreads"
THREAD_FOUND="yes"
fi
elif test "$ac_linux_target" = "yes"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel"; then
THREAD_CFLAGS="-pthread -D_MT -D_THREAD_SAFE"
THREAD_LIBS="-pthread"
THREAD_FOUND="yes"
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
THREAD_CFLAGS="-mt"
THREAD_LIBS="-mt"
THREAD_FOUND="yes"
fi
fi
if test "$THREAD_FOUND" = "no"; then
AC_MSG_NOTICE([Cannot detect compiler thread support. Set CXXFLAGS and LDFLAGS manually.])
else
AC_MSG_NOTICE([CFLAGS: $THREAD_CFLAGS; LIBS: $THREAD_LIBS])
CXXFLAGS="$CXXFLAGS $THREAD_CFLAGS"
LIBS="$LIBS $THREAD_LIBS"
fi
# enable / disable compilation of tests
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests],
[enable compilation of test programs (default: no)]), [with_tests=${enableval}], [with_tests=no])
# export to automake.am-s
AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"])
# -------------------------------------------------------------------------------------
@@ -272,8 +159,8 @@ pkg_modules="gtkmm-2.4 >= 2.6.0 gthread-2.0 >= 2.0"
gtkmm_found="no"
PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [
AC_DEFINE([ENABLE_GLIB], [], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [], [Whether GLibmm is installed])
AC_DEFINE([ENABLE_GLIB], [1], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [1], [Whether GLibmm is installed])
gtkmm_found="yes"
], [
AC_MSG_WARN([$GTKMM_PKG_ERRORS])
@@ -290,68 +177,15 @@ PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [
CXXFLAGS="$GTKMM_CFLAGS $CXXFLAGS"
LIBS="$GTKMM_LIBS $LIBS"
# needed for in-code checks (AC_DEFINE() takes care of these above, no need of them here)
# CXXFLAGS="$CXXFLAGS -DENABLE_GLIB -DENABLE_GLIBMM"
# Detect libglade / gtkbuilder support
# -------------------------------------------------------------------------------------
APP_CHOOSE_GLD_READER([C++], [LIBGLADE_])
# -------------------------------------------------------------------------------------
gld_found=no; # equals to "gtkbuilder" or "libglademm" after tests
#if test "gtkmm_found" = "yes"; then
# Check whether we should use Gtk::Builder (gtkmm >= 2.12) or libglademm.
AC_ARG_ENABLE(libglademm, AS_HELP_STRING([--enable-libglademm],
[use libglademm instead of GtkBuilder (default: auto)]), [with_libglademm=${enableval}], [with_libglademm=auto])
# try gtkbuilder first
if test "$with_libglademm" = "no" || test "$with_libglademm" = "auto"; then
PKG_CHECK_EXISTS([gtkmm-2.4 >= 2.12.0], [gld_found=gtkbuilder])
fi
if test "$with_libglademm" = "no" && test "$gld_found" = "no"; then
AC_MSG_ERROR([GtkBuilder support not found (you need gtkmm version 2.12 or higher). Try using libglademm instead.])
fi
# libglade specified, or auto and no gtkbuilder. gtkmm2.4-compatible libglade is >= 2.4.0.
if test "$gld_found" = "no"; then
PKG_CHECK_MODULES([LIBGLADEMM], [libglademm-2.4 >= 2.4.0], [gld_found="libglademm"],
[AC_MSG_WARN([$LIBGLADEMM_PKG_ERRORS])])
fi
if test "$with_libglademm" = "yes" && test "$gld_found" = "no"; then
AC_MSG_ERROR([Libglademm not found. Try building with GtkBuilder support (you need gtkmm version 2.12 or higher).])
fi
# still not found.
if test "$gld_found" = "no"; then
AC_MSG_WARN([Neither GtkBuilder nor libglademm found. The program will be unable to compile fully.])
fi
AC_MSG_NOTICE([Glade file support through: $gld_found])
# merge libglademm flags
if test "$gld_found" = "libglademm"; then
CXXFLAGS="$LIBGLADEMM_CFLAGS $CXXFLAGS"
LIBS="$LIBGLADEMM_LIBS $LIBS"
fi
#fi
# export to automake.am-s
AM_CONDITIONAL([ENABLE_LIBGLADEMM], [test "$gld_found" = "libglademm"])
AM_CONDITIONAL([ENABLE_GTKBUILDER], [test "$gld_found" = "gtkbuilder"])
# export to config.h
if test "$gld_found" = "libglademm"; then
AC_DEFINE(ENABLE_LIBGLADEMM, 1, [Use libglademm instead of GtkBuilder])
fi
if test "$gld_found" = "gtkbuilder"; then
AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm])
if test "$app_cv_gld_reader" = "libglade"; then
CFLAGS="$CFLAGS $LIBGLADE_CFLAGS"
CXXFLAGS="$CXXFLAGS $LIBGLADE_CXXFLAGS"
LIBS="$LIBS $LIBGLADE_LIBS"
fi
@@ -360,25 +194,27 @@ fi
# -------------------------------------------------------------------------------------
pkg_modules="libpcre"
PCRE_CFLAGS="";
PCRE_LIBS="";
PKG_CHECK_MODULES(PCRE, [$pkg_modules])
# try pkg-config first
PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no])
#CFLAGS="$PCRE_CFLAGS $CFLAGS"
CXXFLAGS="$PCRE_CFLAGS $CXXFLAGS"
LIBS="$PCRE_LIBS $LIBS"
# if not found, try pcre-config (solaris had it but not pkg-config).
if test "$pcre_found" = "no"; then
AC_PATH_PROG(pcre_config_binary, [pcre-config])
if test "x$pcre_config_binary" != "x"; then
PCRE_CFLAGS="`pcre-config --cflags`"
PCRE_LIBS="`pcre-config --libs`"
else
# AC_MSG_ERROR([Could not find pcre through pkg-config and pcre-config. Make sure the pcre libraries are installed])
AC_MSG_ERROR([$PCRE_PKG_ERRORS])
fi
fi
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# enable / disable compilation of tests
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests],
[enable compilation of test programs (default: no)]), [with_tests=${enableval}], [with_tests=no])
# export to automake.am-s
AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"])
CFLAGS="$CFLAGS $PCRE_CFLAGS"
CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS"
LIBS="$LIBS $PCRE_LIBS"
# -------------------------------------------------------------------------------------
@@ -401,7 +237,7 @@ RES_DIST="" # only files in "res" directory, EXTRA_DIST
RES_TARGETS="" # entry for each file in makefile
RES_GLD_EXT="glade"
if test "$gld_found" = "gtkbuilder"; then
if test "$app_cv_gld_reader" = "gtkbuilder"; then
RES_GLD_EXT="ui"
fi
@@ -468,7 +304,8 @@ _AM_SUBST_NOTMAKE([RES_TARGETS])
# -------------------------------------------------------------------------------------
# Export some configure variables as defines for the source code
# Export some configure variables as defines for the source code.
# Note: Autoconf manual says that AC_DEFINE won't work here.
ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
-DPACKAGE_SYSCONF_DIR=\"\\\"\$(sysconfdir)\\\"\" \
@@ -478,10 +315,6 @@ ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Top src dir should always be in include path, before other includes.
# Add global configuration file. Note: It must be searched in top_builddir
# first (that's why -I is the first flag), for precompiled headers to work.
@@ -489,15 +322,20 @@ 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 doesn't work with sun and pgi, but works with intel and gcc.
# we don't whitelist intel/gcc, because there are some gcc-frontend-based
# compilers we don't want to break (pathscale, gccfss (gcc frontend / sun backend), etc...).
if test "$ax_cv_cxx_compiler_vendor" != "sun" && test "$ax_cv_cxx_compiler_vendor" != "portland"; then
# -include works with gcc, intel and pathscale, but doesn't work with sun and pgi.
# Note: some gcc-frontend-based compilers may have to be added here
# (e.g. gccfss (gcc frontend / sun backend), etc...).
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel" \
|| test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
# This auto-includes this file when compiling any file. Benefits include pch support.
ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -include global_macros.h"
else # sun, pgi, others...
# This forces the file to be included manually.
ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -DHZ_NO_COMPILER_AUTOINCLUDE"
fi
#CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS"
# our flags must be before all the others
#CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS"
CXXFLAGS="$ADDITIONAL_INCLUDES $CXXFLAGS"
@@ -506,23 +344,12 @@ CXXFLAGS="$ADDITIONAL_INCLUDES $CXXFLAGS"
# -------------------------------------------------------------------------------------
# Append compiler flags to CXXFLAGS, so that they override
# whatever the user has set.
CXXFLAGS="$CXXFLAGS $COMPILER_CXXFLAGS"
LIBS="$LIBS $COMPILER_LDFLAGS"
# final setup
# This macro should be defined by ./configure automatically.
# However, sometimes it's not (e.g. win32, why?), so we define it manually.
CXXFLAGS="$CXXFLAGS -DHAVE_CONFIG_H"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# final setup
#CFLAGS="$CFLAGS $USER_INCLUDES"
#CXXFLAGS="$CXXFLAGS $USER_INCLUDES"
#LIBS="$LIBS $USER_LDFLAGS"
@@ -535,6 +362,8 @@ AC_SUBST(LIBS)
AC_SUBST(LDFLAGS)
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
@@ -551,11 +380,13 @@ AC_SUBST(LDFLAGS)
# print'em out for debug purposes
# print them out for debug purposes
AC_MSG_NOTICE([
CXXFLAGS:
$CXXFLAGS
LDFLAGS:
$LDFLAGS
LIBS:
$LIBS
])
@@ -566,19 +397,27 @@ $LIBS
AC_SUBST(AUTODIRS)
# ----------------------------------------------------
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
AC_CONFIG_FILES([ data/gsmartcontrol_gnome.desktop data/gsmartcontrol_kde.desktop \
data/gsmartcontrol_other.desktop \
gsmartcontrol.spec version compilation_flags ])
AC_CONFIG_FILES([data/gsmartcontrol_root.sh], [chmod +x data/gsmartcontrol_root.sh])
# these are all the makefiles to generate
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile \
src/libdebug/Makefile src/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])
AC_OUTPUT

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -34,3 +34,7 @@ pixmaps_DATA = 48/gsmartcontrol.png
EXTRA_DIST = $(appdata_DATA) $(pixmaps_DATA)
bin_SCRIPTS = gsmartcontrol_root.sh
CLEANFILES = $(bin_SCRIPTS)
@@ -7,8 +7,7 @@ OnlyShowIn=GNOME;
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
@@ -21,6 +20,8 @@ Comment=Monitor and control SMART data on hard disks
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions. Some desktop files used gksu,
# but it doesn't seem available on my system.
Exec=gnomesu -c "@prefix@/bin/gsmartcontrol"
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol_root.sh" gnome
# Build service rpmlint gives errors without this
X-SuSE-translate=false
@@ -7,8 +7,7 @@ OnlyShowIn=KDE;
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
@@ -26,4 +25,7 @@ Icon=gsmartcontrol
#X-KDE-SubstituteUID=true
#X-KDE-RootOnly=true
Exec=kdesu "@prefix@/bin/gsmartcontrol"
Exec="@prefix@/bin/gsmartcontrol_root.sh" kde
# Build service rpmlint gives errors without this
X-SuSE-translate=false
@@ -7,8 +7,7 @@ NotShowIn=GNOME;KDE;
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
@@ -21,8 +20,8 @@ Comment=Monitor and control SMART data on hard disks
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions using gnomesu (from libgnomesu).
# There is also kdesu (kdebase3-runtime, kdebase4-runtime), in different paths.
# There is also xdg-su, which doesn't work at all on my machine.
Exec=gnomesu -c "@prefix@/bin/gsmartcontrol"
# Exec=kdesu -u root "@prefix@/bin/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
@@ -0,0 +1,82 @@
#!/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

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -1,10 +1,14 @@
#!/bin/sh
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# We use #!/bin/bash of #!/bin/sh because freebsd (tested with 6.3) in its
# infinite wisdom has csh-like /bin/sh (even though the SUS says it
# must be bourne-compatible).
if [ "$1" == "" ] | [ "$2" == "" ] | [ "$3" == "" ]; then
echo "Usage: $0 <input_file> <cpp_file> <symbol_name>";
@@ -66,7 +66,7 @@
<cflags/>
<cxxflags/>
<envvars/>
<configargs>--enable-default-gcc-options --enable-gcc-optimize --enable-gcc-pch --enable-tests</configargs>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests</configargs>
<topsourcedir/>
<cppflags/>
<ldflags/>
@@ -76,7 +76,7 @@
<f77flags/>
</optimized>
<debuggy>
<configargs>--enable-default-gcc-options --enable-gcc-debug --enable-tests</configargs>
<configargs>--disable-user-flags --enable-debug-options --enable-tests</configargs>
<builddir>debuggy</builddir>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
@@ -94,24 +94,24 @@
</debuggy>
<icc>
<envvars/>
<configargs>--disable-default-gcc-options --enable-tests</configargs>
<configargs>--disable-user-flags --enable-tests</configargs>
<builddir>icc</builddir>
<topsourcedir/>
<cppflags/>
<ldflags>-s -static-libgcc -static-intel</ldflags>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>icc</ccompilerbinary>
<cxxcompilerbinary>icpc</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-w1</cflags>
<cxxflags>-g0 -O3 -m32 -ftrapuv -fstack-security-check -early-template-check -static-libgcc -static-intel -Wcomment -Wdeprecated -Wextra-tokens -Wformat -Wmissing-prototypes -Wnon-virtual-dtor -Wpointer-arith -Wreturn-type -Wshadow -Wtrigraphs -Wuninitialized -w1</cxxflags>
<cflags/>
<cxxflags/>
<f77flags/>
</icc>
<win32>
<envvars/>
<configargs>--enable-default-gcc-options --enable-gcc-optimize --enable-gcc-pch --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>--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>
<builddir>win32</builddir>
<topsourcedir/>
<cppflags/>
@@ -119,8 +119,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>&quot;i386-mingw32msvc-gcc -mms-bitfields&quot;</ccompilerbinary>
<cxxcompilerbinary>&quot;i386-mingw32msvc-g++ -mms-bitfields&quot;</cxxcompilerbinary>
<ccompilerbinary>i386-mingw32msvc-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32msvc-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -130,7 +130,7 @@
<envvars>
<envvar value="/opt/sun/sunstudio12/bin:$PATH" name="PATH" />
</envvars>
<configargs>--disable-default-gcc-options --enable-tests</configargs>
<configargs>--enable-tests</configargs>
<builddir>suncc</builddir>
<topsourcedir/>
<cppflags/>
@@ -141,8 +141,8 @@
<ccompilerbinary>suncc</ccompilerbinary>
<cxxcompilerbinary>sunCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-DDEBUG -DDEBUG_BUILD</cflags>
<cxxflags>+w -errtags -erroff=notemsource,notused -features=extensions -DHZ_FORCE_GLOBAL_MACROS -DDEBUG -DDEBUG_BUILD -DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<cflags>-errwarn=%all</cflags>
<cxxflags>-DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<f77flags/>
</suncc>
<portland>
@@ -151,7 +151,7 @@
<builddir>portland</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ldflags>-s</ldflags>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
@@ -159,7 +159,7 @@
<cxxcompilerbinary>pgCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-Minform=warn -Bstatic_pgi</cflags>
<cxxflags>-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_FORCE_GLOBAL_MACROS</cxxflags>
<cxxflags>-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE</cxxflags>
<f77flags/>
</portland>
</configurations>
@@ -10,6 +10,12 @@ Source: gsmartcontrol-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Summary: GSmartControl - Hard Disk Health Inspection Tool
# Support build service rpmlint. Disabled - the build service
# stopped recognizing the flag for some architectures.
# %if 0%{?opensuse_bs} && 0%{?suse_version} >= 1010
# Support: rpmlint-bs
# %endif
# Dependencies for various distributions. The actual deps are:
# smartmontools, pcre, (gtkmm2 >= 2.12 || (gtkmm2 >= 2.6.0 && libglademm >= 2.4.0))
@@ -17,10 +23,10 @@ Summary: GSmartControl - Hard Disk Health Inspection Tool
# For non-specified distributions we don't specify any dependencies to avoid errors.
# SUSE, OpenSUSE. SLES also defines the correct suse_version.
# SUSE / OpenSUSE. SLES also defines the correct suse_version.
%if 0%{?suse_version}
%if %suse_version >= 1030
%if 0%{?%suse_version} >= 1030
Requires: smartmontools, pcre, gtkmm2 >= 2.12.0
BuildRequires: gcc-c++ libstdc++-devel pcre-devel, gtkmm2-devel >= 2.12.0
%else
@@ -83,7 +89,7 @@ if [ "$numprocs" = "0" ]; then
fi
make -j$numprocs
#make
# make
%install
@@ -99,13 +105,13 @@ rm -rf %buildroot
%defattr(-,root,root)
%attr(0755,root,root) %{_bindir}/*
#%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
# %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
%doc %{_datadir}/doc/gsmartcontrol
%{_datadir}/gsmartcontrol
%{_datadir}/gsmartcontrol/*
%{_datadir}/applications/*
# %{_datadir}/gsmartcontrol/*
%{_datadir}/applications/*.desktop
%{_datadir}/icons/*
%{_datadir}/pixmaps/*
-1
View File
@@ -1 +0,0 @@
See NEWS file.
@@ -1,16 +0,0 @@
AC_DEFUN([AC_CXX_VAR_FUNC],
[AC_CACHE_CHECK(whether $CXX recognizes __func__,
ac_cv_cxx_var_func,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[const char* s = __func__;]])],[ac_cv_cxx_var_func=yes],[ac_cv_cxx_var_func=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_var_func" = yes; then
AC_DEFINE(HAVE_CXX__FUNC,,[Define if the C++ complier supports __func__])
fi
])
@@ -1,408 +0,0 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <ios> // std::boolalpha
// #include <glibmm/fileutils.h> // Dir
#include <algorithm> // std::find
#include <cstdio> // std::fgets()
#include "hz/debug.h"
#include "hz/fs_dir.h"
#include "hz/fs_file.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "storage_detector.h"
namespace {
// inline std::string detect_drives_linux_sys_block(std::vector<StorageDeviceRefPtr>& drives)
// {
// hz::Dir dir("/sys/block");
// }
// 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...\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).
/*
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 partitions file...\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]+$/");
blacklist.push_back("/loop[0-9]*$/");
blacklist.push_back("/part[0-9]+$/");
blacklist.push_back("/p[0-9]+$/");
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();
}
/*
// FreeBSD, etc... .
// Linux devices are not listed because /proc/partitions should always exist there.
inline std::string detect_drives_dev(std::vector<std::string>& devices)
{
return std::string();
}
inline std::string detect_drives_win32(std::vector<std::string>& devices)
{
return std::string();
}
*/
}
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
{
debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n");
std::vector<std::string> devices;
std::string error_msg;
bool found = false;
// Try each one and move to next if it fails.
if (!found) {
error_msg = detect_drives_linux_udev_byid(devices); // linux udev
// we check for devices vector emptiness because it could be a dummy directory
// with no files, so treat it as an error.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
if (!found) {
error_msg = detect_drives_linux_proc_partitions(devices); // linux /proc/partitions as fallback.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
/*
if (!found) {
error_msg = detect_drives_dev(devices); // bsd, etc... . scans /dev.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
*/
if (!found) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n");
return error_msg; // last error message should be ok.
}
for (std::vector<std::string>::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) {
std::string dev = *iter;
// try to match against patterns
// for (unsigned int i = 0; i < match_patterns_.size(); i++) {
// try to match against general filter
// if (!app_pcre_match(match_patterns_[i], dev))
// continue;
// 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
blacked = true;
break;
}
}
StorageDeviceRefPtr drive(new StorageDevice(dev));
debug_out_info("app", "Found device: \"" << drive->get_device() << "\".\n");
if (!blacked) {
drives.push_back(drive);
// break; // no need to match other patters, go to next device
} else { // blacklisted
debug_out_info("app", "Device \"" << drive->get_device() << "\" is blacklisted, ignoring.\n");
// break; // go to next device
}
// }
}
debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n");
return std::string();
}
std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> smartctl_ex, bool return_first_error)
{
fetch_data_errors_.clear();
fetch_data_error_outputs_.clear();
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() + "...");
// 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);
// normally we skip drives with errors - possibly scsi, etc...
if (return_first_error && !error_msg.empty())
return error_msg;
if (!error_msg.empty()) {
// use original executor error if present (permits matches by our users).
// if (!smartctl_ex->get_error_msg().empty())
// error_msg = smartctl_ex->get_error_msg();
fetch_data_errors_.push_back(error_msg);
fetch_data_error_outputs_.push_back(smartctl_ex->get_stdout_str());
}
debug_out_dump("app", "Device information for " << drive->get_device() << ":\n"
<< "\tModel: " << drive->get_model_name() << "\n"
<< "\tType: " << StorageDevice::get_type_name(drive->get_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n"
);
}
return std::string();
}
std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
std::string error_msg = detect(put_drives_here);
if (error_msg.empty())
fetch_basic_data(put_drives_here, smartctl_ex, false); // ignore its errors, there may be plenty of them.
return error_msg;
}
@@ -1,7 +1,6 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
bin_PROGRAMS = gsmartcontrol
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
@@ -25,6 +25,8 @@ 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
spawn_test_SOURCES = spawn_test.cpp
# Build them first
SUBDIRS=../libdebug ../pcrecpp
@@ -33,7 +35,7 @@ SUBDIRS=../libdebug ../pcrecpp
noinst_PROGRAMS =
if ENABLE_TESTS
noinst_PROGRAMS += smartctl_executor_test smartctl_parser_test \
storage_detector_test
storage_detector_test spawn_test
endif
tests: $(noinst_PROGRAMS)
@@ -10,7 +10,9 @@
#include <string>
#include <gtkmm/window.h>
#if defined ENABLE_LIBGLADEMM
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
#if defined ENABLE_LIBGLADE
#include <libglademm.h>
#elif defined ENABLE_GTKBUILDER
#include <gtkmm/builder.h>
@@ -42,7 +44,7 @@
// Or, if you're using compiled-in buffers, it will make them available.
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".glade", UIResDataBase); \
@@ -51,7 +53,7 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#elif defined ENABLE_GTKBUILDER
@@ -63,7 +65,7 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#else // none, just avoid errors
@@ -75,14 +77,14 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#endif
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
typedef Glib::RefPtr<Gnome::Glade::Xml> app_ui_res_ref_t;
@@ -215,7 +217,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
typedef hz::InstanceManager<Child, MultiInstance> instance_class;
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
friend class Gnome::Glade::Xml; // allow construction through libglade
#elif defined ENABLE_GTKBUILDER
friend class Gtk::Builder; // allow construction through gtkbuilder
@@ -230,7 +232,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
std::string error;
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
app_ui_res_ref_t ui;
#elif defined ENABLE_GTKBUILDER
app_ui_res_ref_t ui = Gtk::Builder::create();
@@ -297,7 +299,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
Glib::Object* lookup_object(const Glib::ustring& name)
{
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
return static_cast<Glib::Object*>(lookup_widget(name)); // all glade objects are widgets, it's an upcast
#elif defined ENABLE_GTKBUILDER
return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get().
@@ -8,19 +8,16 @@
#include <cerrno> // errno (not std::errno, it may be a macro)
#ifdef _WIN32
#include <io.h> // close()
// #include <io.h> // close()
#else
#include <sys/wait.h> // waitpid()'s W* macros
#include <unistd.h> // close()
// #include <unistd.h> // close()
#endif
#include <glibmm/shell.h>
#include <glibmm/miscutils.h> // get_current_dir()
#include <glibmm/convert.h>
#include "hz/tls.h"
#include "hz/tls_policy_glib.h"
#include "hz/debug.h"
#include "hz/string_num.h" // hz::number_to_string()
#include "cmdex.h"
@@ -41,40 +38,40 @@ extern "C" {
// NOTE: These functions are called in a separate threads!
inline void command_executor_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
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 command_executor_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean command_executor_on_channel_io_stderr_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stderr_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
inline gboolean command_executor_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_buffered(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean command_executor_on_channel_io_stderr_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
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 command_executor_on_buffered_source_destroy(gpointer data);
inline void cmdex_on_buffered_source_destroy(gpointer data);
// this is executed in another thread!
inline gboolean command_executor_on_term_timeout(gpointer data)
inline gboolean cmdex_on_term_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
@@ -84,7 +81,7 @@ extern "C" {
// this is executed in another thread!
inline gboolean command_executor_on_kill_timeout(gpointer data)
inline gboolean cmdex_on_kill_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
@@ -104,114 +101,135 @@ bool Cmdex::execute()
{
ScopedLock locker(object_mutex_);
if (this->running || this->stopped_cleanup_needed(false))
if (this->running_ || this->stopped_cleanup_needed(false))
return false;
cleanup_members();
clear_errors(false); // don't lock
str_stdout.clear();
str_stderr.clear();
str_stdout_.clear();
str_stderr_.clear();
std::string cmd = command_exec + " " + command_args;
std::deque<std::string> child_argv;
std::string cmd = command_exec_ + " " + command_args_;
try {
child_argv = Glib::shell_parse_argv(cmd);
}
catch (Glib::ShellError& e) {
push_error(Error<Glib::ShellError>("gshell", ErrorLevel::error, e, e.what()), false);
// 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);
g_strfreev(argvp);
return false;
}
// Reset the locale - otherwise it may mangle the output.
bool old_lang_set = false;
std::string old_lang = Glib::getenv("LANG", old_lang_set);
// 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)
Glib::setenv("LANG", "C");
g_setenv("LANG", "C", true);
try {
// execute the command
debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n");
debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n");
// Execute the command
gchar* curr_dir = g_get_current_dir();
GError* spawn_error = 0;
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);
Glib::spawn_async_with_pipes(Glib::get_current_dir(), child_argv,
Glib::SPAWN_SEARCH_PATH | Glib::SPAWN_DO_NOT_REAP_CHILD,
sigc::slot<void>(), &this->pid,
0, &fd_stdout, &fd_stderr);
}
catch (Glib::SpawnError& e) {
push_error(Error<Glib::SpawnError>("gspawn", ErrorLevel::error, e, e.what()), false);
if (old_lang_set)
Glib::setenv("LANG", old_lang);
g_setenv("LANG", old_lang.c_str(), true);
return false;
}
g_free(curr_dir);
g_strfreev(argvp);
if (old_lang_set)
Glib::setenv("LANG", old_lang);
g_setenv("LANG", old_lang.c_str(), true);
this->timer.start(); // start the timer
g_timer_start(timer_); // start the timer
#ifdef _WIN32
channel_stdout = Glib::IOChannel::create_from_win32_fd(fd_stdout);
channel_stderr = Glib::IOChannel::create_from_win32_fd(fd_stderr);
channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_);
channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_);
#else
channel_stdout = Glib::IOChannel::create_from_fd(fd_stdout);
channel_stderr = Glib::IOChannel::create_from_fd(fd_stderr);
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 Glib::locale_to_utf8() or Glib::convert() afterwards.
// If using locales, call g_locale_to_utf8() or g_convert() afterwards.
// std::string charset;
// Glib::get_charset(charset);
try {
channel_stdout->set_encoding(""); // binary IO
channel_stderr->set_encoding(""); // binary IO
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
GIOFlags channel_flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
Glib::IOFlags flags = ~Glib::IO_FLAG_NONBLOCK;
channel_stdout->set_flags(flags);
channel_stderr->set_flags(flags);
channel_stdout->set_buffer_size(channel_stdout_buffer_size);
channel_stderr->set_buffer_size(channel_stderr_buffer_size);
// Note about GError's here:
// What do we do? The command is already running, so let's ignore these
// errors - it's better to get a slightly mangled buffer than to abort the
// command in the mid-run.
if (channel_stdout_) {
// Since we invoke shutdown() manually before unref(), this would cause
// 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_buffer_size(channel_stdout_, channel_stdout_buffer_size_);
}
catch (Glib::IOChannelError& e) {
// What do we do? The command is already running, so let's ignore these
// errors - it's better to get a slightly mangled buffer than to abort the
// command in the mid-run.
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_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.
// this doesn't really work
// Glib::signal_io().connect(sigc::bind(sigc::ptr_fun(&io_handler), this), channel_stdout, cond);
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
if (this->stdout_make_str_as_available) {
this->event_source_id_stdout = g_io_add_watch_full(channel_stdout->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stdout_as_available, this, NULL);
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->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stdout_buffered, this, command_executor_on_buffered_source_destroy);
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);
}
if (this->stderr_make_str_as_available) {
this->event_source_id_stderr = g_io_add_watch_full(channel_stderr->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stderr_as_available, 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->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stderr_buffered, this, command_executor_on_buffered_source_destroy);
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);
}
@@ -220,11 +238,11 @@ bool Cmdex::execute()
// 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(static_cast<GPid>(this->pid), &command_executor_child_watch_handler, this);
g_child_watch_add(this->pid_, &cmdex_child_watch_handler, this);
this->running = true; // process is running now.
this->running_ = true; // process is running now.
// actually, it may be already stopped, but the watcher function is blocked
// until we exit, so the actions are serialized.
@@ -239,15 +257,15 @@ bool Cmdex::try_stop(hz::signal_t sig)
{
ScopedLock locker(object_mutex_);
if (!this->running || this->pid <= 0)
if (!this->running_ || this->pid_ <= 0)
return false;
// other variants: SIGHUP(1) (terminal closed), SIGINT(2) (Ctrl-C),
// SIGKILL(9) (kill).
// Note that SIGKILL cannot be trapped by any process.
if (process_signal_send(this->pid, sig) == 0) { // success
this->kill_signal_sent = static_cast<int>(sig); // just the number to compare later.
if (process_signal_send(this->pid_, sig) == 0) { // success
this->kill_signal_sent_ = static_cast<int>(sig); // just the number to compare later.
return true; // the rest is done by a handler
}
@@ -265,16 +283,16 @@ 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
if (!this->running_) // process not running
return;
unset_stop_timeouts(false);
if (term_timeout_msec != 0)
event_source_id_term = g_timeout_add(term_timeout_msec, &command_executor_on_term_timeout, this);
event_source_id_term = g_timeout_add(term_timeout_msec, &cmdex_on_term_timeout, this);
if (kill_timeout_msec != 0)
event_source_id_kill = g_timeout_add(kill_timeout_msec, &command_executor_on_kill_timeout, this);
event_source_id_kill = g_timeout_add(kill_timeout_msec, &cmdex_on_kill_timeout, this);
}
@@ -307,7 +325,7 @@ 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(false)) // huh?
return;
// remove stop timeout callbacks
@@ -315,22 +333,23 @@ void Cmdex::stopped_cleanup()
// various statuses (see waitpid (2)):
if (WIFEXITED(waitpid_status)) { // exited normally
int exit_status = WEXITSTATUS(waitpid_status);
if (WIFEXITED(waitpid_status_)) { // exited normally
int exit_status = WEXITSTATUS(waitpid_status_);
if (exit_status != 0) {
// translate the exit_code into a message
Glib::ustring msg = signal_exit_status_translator.emit(exit_status);
std::string msg = (translator_func_ ? translator_func_(exit_status, translator_func_data_)
: "[no translator function, exit code: " + hz::number_to_string(exit_status));
push_error(Error<int>("exit", ErrorLevel::warn, exit_status, msg), false);
}
} else {
if (WIFSIGNALED(waitpid_status)) { // exited by signal
int sig_num = WTERMSIG(waitpid_status);
if (WIFSIGNALED(waitpid_status_)) { // exited by signal
int sig_num = WTERMSIG(waitpid_status_);
// If it's not our signal, treat as error.
// Note: they will never match under win32
if (sig_num != this->kill_signal_sent) {
if (sig_num != this->kill_signal_sent_) {
push_error(Error<int>("signal", ErrorLevel::error, sig_num), false);
} else { // it's our signal, treat as warning
push_error(Error<int>("signal", ErrorLevel::warn, sig_num), false);
@@ -338,11 +357,11 @@ void Cmdex::stopped_cleanup()
}
}
Glib::spawn_close_pid(this->pid); // needed to avoid zombies
g_spawn_close_pid(this->pid_); // needed to avoid zombies
cleanup_members();
this->running = false;
this->running_ = false;
}
@@ -357,41 +376,45 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
self->lock();
self->timer.stop(); // stop the timer
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();
// Glib::usleep(75*1000); // 75ms
// 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
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.
// 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) {
GSource* source_stdout = g_main_context_find_source_by_id(NULL, self->event_source_id_stdout);
if (self->event_source_id_stdout_) {
GSource* source_stdout = g_main_context_find_source_by_id(NULL, self->event_source_id_stdout_);
if (source_stdout)
g_source_destroy(source_stdout);
}
if (self->event_source_id_stderr) {
GSource* source_stderr = g_main_context_find_source_by_id(NULL, self->event_source_id_stderr);
if (self->event_source_id_stderr_) {
GSource* source_stderr = g_main_context_find_source_by_id(NULL, self->event_source_id_stderr_);
if (source_stderr)
g_source_destroy(source_stderr);
}
// Cose std pipes
close(self->fd_stdout);
close(self->fd_stderr);
// Close std pipes.
// The channel closes them now.
// close(self->fd_stdout_);
// close(self->fd_stderr_);
self->signal_exited.emit(); // ALL slots are called in non-main thread!
// 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;
@@ -421,45 +444,42 @@ gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
// 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;
char buf[count] = {0};
gchar buf[count] = {0};
Cmdex::ScopedLock locker(self->object_mutex_);
Glib::RefPtr<Glib::IOChannel> channel;
GIOChannel* channel = 0;
std::string* output_str = 0;
if (type == channel_type_stdout) {
channel = self->channel_stdout;
output_str = &self->str_stdout;
channel = self->channel_stdout_;
output_str = &self->str_stdout_;
} else if (type == channel_type_stderr) {
channel = self->channel_stderr;
output_str = &self->str_stderr;
channel = self->channel_stderr_;
output_str = &self->str_stderr_;
}
try { // read() may throw something
// while there's anything to read, read it
do {
gsize bytes_read = 0;
Glib::IOStatus status = channel->read(buf, count, bytes_read);
if (bytes_read)
output_str->append(buf, bytes_read);
// while there's anything to read, read it
do {
GError* channel_error = 0;
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(channel, buf, count, &bytes_read, &channel_error);
if (bytes_read)
output_str->append(buf, bytes_read);
// IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable.
if (status == Glib::IO_STATUS_ERROR || status == Glib::IO_STATUS_EOF) {
continue_events = false;
break;
}
} while (channel->get_buffer_condition() & Glib::IO_IN);
if (channel_error) {
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(channel) & G_IO_IN);
}
catch (Glib::IOChannelError& e) {
self->push_error(Error<Glib::IOChannelError>("giochannel", ErrorLevel::error, e, e.what()), false);
}
catch (Glib::ConvertError& e) { // no conversion in binary mode, shouldn't happen
self->push_error(Error<void>("custom", ErrorLevel::error,
"Cmdex::on_channel_io_as_available(): Glib::ConvertError thrown."), false);
}
// false if the source should be removed, true otherwise.
return continue_events;
@@ -487,7 +507,7 @@ namespace {
// 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 command_executor_on_buffered_source_destroy(gpointer data)
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.
@@ -520,57 +540,45 @@ gboolean Cmdex::on_channel_io_buffered(GIOChannel* source,
const gsize count = 1;
static char buf[count] = {0};
// Glib::RefPtr<Glib::IOChannel> channel = Glib::wrap(source); // this segfaults on large data (?)
// while there's anything to read, read it
GError* error = 0;
do {
GError* channel_error = 0;
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(source, buf, count, &bytes_read, &error);
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;
}
if (error)
break;
} while (g_io_channel_get_buffer_condition(source) & G_IO_IN);
if (!continue_events || error) { // these conditions need locking
if (!continue_events) { // need locking
Cmdex::ScopedLock locker(self->object_mutex_);
if (error) {
try {
Glib::Error::throw_exception(error); // convert GError to exception for easy storage.
}
catch (Glib::IOChannelError& e) {
self->push_error(Error<Glib::IOChannelError>("giochannel", ErrorLevel::error, e, e.what()), false);
}
catch (Glib::ConvertError& e) { // no conversion in binary mode, shouldn't happen
self->push_error(Error<void>("custom", ErrorLevel::error,
"Cmdex::on_channel_io_buffered(): Glib::ConvertError thrown."), false);
}
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;
}
if (!continue_events) {
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.
}
// 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;
@@ -8,12 +8,7 @@
#define APP_CMDEX_H
#include <string>
#include <sigc++/sigc++.h>
#include <glibmm/iochannel.h>
#include <glibmm/thread.h>
#include <glibmm/timer.h> // Timer
#include <glibmm/spawn.h> // Pid
#include <glib.h> // G*, g_*
#include <glib.h>
#include "hz/process_signal.h" // hz::SIGNAL_*, hz::process_signal_send (cpp), win32's W* (cpp)
#include "hz/error_holder.h"
@@ -41,7 +36,7 @@ from the _main_ thread.
// inheriting from sigc::trackable automatically disconnects this class's
// slots upon destruction.
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable {
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
public:
@@ -51,24 +46,26 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorScopedLock ScopedLock;
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorLockPolicy LockPolicy;
typedef std::string (*exit_status_translator_func_t)(int, void*);
typedef void (*exited_callback_func_t)(void*);
Cmdex(
//const sigc::slot<Glib::ustring, int>& slot_exit_status_translator,
const sigc::slot<void>& slot_exited = sigc::slot<void>()
// exit_status_translator_func_t translator_func,
exited_callback_func_t exited_cb = NULL, void* exited_cb_data = NULL
)
: object_mutex_(error_object_mutex_),
running(false), kill_signal_sent(0), child_watch_handler_called(false),
pid(0), waitpid_status(0),
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_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)
{
// signal_exit_status_translator.connect(slot_exit_status_translator);
if (!slot_exited.empty())
signal_exited.connect(slot_exited);
}
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)
{ }
@@ -79,14 +76,19 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
// This will help if object is destroyed after the command has exited, but before
// stopped_cleanup() has been called.
stopped_cleanup();
g_timer_destroy(timer_);
// no need to destroy the channels - stopped_cleanup() calls
// cleanup_members(), which deletes them.
}
// Call before execute.
void set_command(const std::string& command_exec_, const std::string& command_args_)
void set_command(const std::string& command_exec, const std::string& command_args)
{
command_exec = command_exec_;
command_args = command_args_;
command_exec_ = command_exec;
command_args_ = command_args;
}
@@ -130,7 +132,7 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
bool stopped_cleanup_needed(bool do_lock = true)
{
ScopedLock locker(object_mutex_, do_lock);
return (child_watch_handler_called);
return (child_watch_handler_called_);
}
@@ -140,7 +142,7 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
bool is_running() const
{
ScopedLock locker(object_mutex_);
return running;
return running_;
}
@@ -162,9 +164,9 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
{
ScopedLock locker(object_mutex_);
if (stdout_buffer_size)
channel_stdout_buffer_size = stdout_buffer_size; // 100K by default
channel_stdout_buffer_size_ = stdout_buffer_size; // 100K by default
if (stderr_buffer_size)
channel_stderr_buffer_size = stderr_buffer_size; // 10K by default
channel_stderr_buffer_size_ = stderr_buffer_size; // 10K by default
}
@@ -179,25 +181,25 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
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;
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(),
// 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);
// debug_out_dump("app", str_stdout_);
if (clear_existing) {
std::string ret = str_stdout;
str_stdout.clear();
std::string ret = str_stdout_;
str_stdout_.clear();
return ret;
}
return str_stdout;
return str_stdout_;
}
@@ -206,33 +208,42 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
{
ScopedLock locker(object_mutex_);
if (clear_existing) {
std::string ret = str_stderr;
str_stderr.clear();
std::string ret = str_stderr_;
str_stderr_.clear();
return ret;
}
return str_stderr;
return str_stderr_;
}
// 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(const sigc::slot<Glib::ustring, int>& slot_exit_status_translator)
void set_exit_status_translator(exit_status_translator_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
signal_exit_status_translator.slots().erase(signal_exit_status_translator.slots().begin(), signal_exit_status_translator.slots().end());
signal_exit_status_translator.connect(slot_exit_status_translator);
translator_func_ = func;
translator_func_data_ = user_data;
}
// Returns sigc::connection; you must use lock() / unlock() before modifying it.
// NOTE: The slot will be called in non-main thread! (the call is enclosed
// 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.
sigc::connection connect_signal_exited(const sigc::slot<void>& slot_exited)
void set_exited_callback(exited_callback_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
return signal_exited.connect(slot_exited);
exited_callback_ = func;
exited_callback_data_ = user_data;
}
@@ -291,16 +302,25 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
void cleanup_members()
{
// ScopedLock locker(object_mutex_);
kill_signal_sent = 0;
child_watch_handler_called = false;
pid = 0;
waitpid_status = 0;
channel_stdout.clear();
channel_stderr.clear();
event_source_id_stdout = 0;
event_source_id_stderr = 0;
fd_stdout = 0;
fd_stderr = 0;
kill_signal_sent_ = 0;
child_watch_handler_called_ = false;
pid_ = 0;
waitpid_status_ = 0;
event_source_id_stdout_ = 0;
event_source_id_stderr_ = 0;
fd_stdout_ = 0;
fd_stderr_ = 0;
if (channel_stdout_) {
g_io_channel_shutdown(channel_stdout_, false, NULL);
g_io_channel_unref(channel_stdout_);
channel_stdout_ = 0;
}
if (channel_stderr_) {
g_io_channel_shutdown(channel_stderr_, false, NULL);
g_io_channel_unref(channel_stderr_);
channel_stderr_ = 0;
}
}
@@ -308,51 +328,53 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
// default command and its args. std::strings, not ustrings.
std::string command_exec; // binary name. NOT affected by cleanup().
std::string command_args; // args that always go with binary. NOT affected by cleanup().
std::string command_exec_; // binary name. NOT affected by cleanup().
std::string command_args_; // args that always go with binary. NOT affected by cleanup().
bool running; // child process is running now. NOT affected by cleanup().
int kill_signal_sent; // command has been sent this signal to terminate
bool child_watch_handler_called; // true after child_watch_handler callback, before stopped_cleanup().
bool running_; // child process is running now. NOT affected by cleanup().
int kill_signal_sent_; // command has been sent this signal to terminate
bool child_watch_handler_called_; // true after child_watch_handler callback, before stopped_cleanup().
Glib::Pid pid; // int in Unix, pointer in win32
int waitpid_status; // after the command is stopped, before cleanup, this will be available.
GPid pid_; // int in Unix, pointer in win32
int waitpid_status_; // after the command is stopped, before cleanup, this will be available.
Glib::Timer timer; // keep track of elapsed time since command execution. not used by this class, but may be handy.
GTimer* timer_; // keep track of elapsed time since command execution. not used by this class, but may be handy.
int event_source_id_term;
int event_source_id_kill;
int fd_stdout;
int fd_stderr;
int fd_stdout_;
int fd_stderr_;
Glib::RefPtr<Glib::IOChannel> channel_stdout;
Glib::RefPtr<Glib::IOChannel> channel_stderr;
GIOChannel* channel_stdout_;
GIOChannel* channel_stderr_;
int channel_stdout_buffer_size; // NOT affected by cleanup().
int channel_stderr_buffer_size; // NOT affected by cleanup().
int channel_stdout_buffer_size_; // NOT affected by cleanup().
int channel_stderr_buffer_size_; // NOT affected by cleanup().
int event_source_id_stdout;
int event_source_id_stderr;
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().
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().
std::string str_stdout_; // NOT affected by cleanup().
std::string str_stderr_; // NOT affected by cleanup().
// signals
// convert command exit status to message string
sigc::signal<Glib::ustring, int> signal_exit_status_translator; // NOT affected by cleanup().
// command exited signal
sigc::signal<void> signal_exited; // NOT affected by cleanup().
exit_status_translator_func_t translator_func_; // NOT affected by cleanup().
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,7 +4,7 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <glibmm/timer.h> // usleep()
#include <glib.h> // g_usleep()
#include "cmdex_sync.h"
@@ -93,7 +93,7 @@ bool CmdexSync::execute()
g_main_context_iteration(NULL, false);
}
Glib::usleep(50*1000); // 50 msec. avoids 100% CPU usage.
g_usleep(50*1000); // 50 msec. avoids 100% CPU usage.
}
// command exited, do a cleanup.
@@ -8,7 +8,7 @@
#define APP_CMDEX_SYNC_H
#include <sigc++/sigc++.h>
#include <glibmm/ustring.h> // ustring
#include <string>
#include "hz/error.h"
#include "hz/intrusive_ptr.h"
@@ -196,9 +196,9 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
return cmdex_.get_stderr_str(clear_existing);
}
void set_exit_status_translator(const sigc::slot<Glib::ustring, int>& slot_exit_status_translator)
void set_exit_status_translator(Cmdex::exit_status_translator_func_t func, void* user_data)
{
cmdex_.set_exit_status_translator(slot_exit_status_translator);
cmdex_.set_exit_status_translator(func, user_data);
}
@@ -209,7 +209,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
return error_msg_;
}
void set_running_msg(const Glib::ustring& msg)
void set_running_msg(const std::string& msg)
{
running_msg_ = msg;
}
@@ -264,7 +264,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
}
Glib::ustring get_running_msg() const
std::string get_running_msg() const
{
return running_msg_;
}
@@ -282,7 +282,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
std::string command_name_;
std::string command_args_;
Glib::ustring running_msg_; // message to show in the dialogs, etc...
std::string running_msg_; // message to show in the dialogs, etc...
int forced_kill_timeout_msec;
@@ -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("--test=" + test_param, smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-t " + test_param, smartctl_ex, output); // --test=
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -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("--abort", smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-X", smartctl_ex, output); // --abort
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -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("--log=selftest", smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("--capabilities", smartctl_ex, output);
// std::string error_msg = drive_->execute_smartctl("-l selftest", smartctl_ex, output); // --log=
std::string error_msg = drive_->execute_smartctl("-c", smartctl_ex, output); // --capabilities
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -9,6 +9,7 @@
#include <string>
#include <stdint.h>
#include <glibmm/timer.h>
#include "hz/intrusive_ptr.h"
@@ -7,14 +7,11 @@
#ifndef SMARTCTL_EXECUTOR_H
#define SMARTCTL_EXECUTOR_H
#include <sigc++/sigc++.h>
#include "cmdex.h"
#include "cmdex_sync.h"
// Executor inherits from sigc::trackable
template<class ExecutorSync>
class SmartctlExecutorGeneric : public ExecutorSync {
@@ -42,8 +39,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
void construct()
{
ExecutorSync::cmdex_.set_exit_status_translator(sigc::mem_fun(this,
&SmartctlExecutorGeneric::translate_exit_status));
ExecutorSync::cmdex_.set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status, NULL);
this->set_error_header("An error occurred while executing smartctl:\n\n");
}
@@ -60,7 +56,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
};
Glib::ustring translate_exit_status(int status)
static std::string translate_exit_status(int status, void* user_data)
{
const char* table[] = {
"Command line did not parse.",
@@ -73,7 +69,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
"The device self-test log contains records of errors."
};
Glib::ustring str;
std::string str;
// check every bit
for (unsigned int i = 0; i <= 7; i++) {
@@ -1,13 +1,14 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include <gtkmm/main.h>
#include "smartctl_executor_gui.h"
// #include "smartctl_executor_gui.h"
#include "smartctl_executor.h"
@@ -16,12 +17,14 @@ int main(int argc, char** argv)
Glib::thread_init();
Gtk::Main m(argc, argv);
// NOTE: Don't use long options (e.g. --info). Use short ones (e.g. -i),
// because long options may be unsupported on some platforms.
// SmartctlExecutor ex("smartctl", "--info /dev/sda");
// SmartctlExecutor ex("smartctl", "-i /dev/sda");
// SmartctlExecutorGui ex("ls", "-l --color=no -R /dev");
// SmartctlExecutorGui ex("lsa", "-1 --color=no /sys/block");
SmartctlExecutorGui ex("../../../0test_binary.sh", "");
// SmartctlExecutor ex("../../../0test_binary.sh", "");
// SmartctlExecutorGui ex("../../../0test_binary.sh", "");
SmartctlExecutor ex("../../../0test_binary.sh", "");
ex.execute();
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#undef HZ_USE_LIBDEBUG
+83
View File
@@ -0,0 +1,83 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include <glibmm.h>
#include <glib.h>
int main(int argc, char** argv)
{
Glib::thread_init();
GPid pid;
int fd_stdout = 0, fd_stderr = 0;
std::string cmd = "iexplore";
// std::vector<std::string> child_argv = Glib::shell_parse_argv(cmd);
gchar* curr_dir = g_get_current_dir();
int argcp = 0; // number of args
gchar** argvp = 0; // args vector
GError* shell_error = 0;
g_shell_parse_argv(cmd.c_str(), &argcp, &argvp, &shell_error);
GError* spawn_error = 0;
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
&pid, 0, &fd_stdout, &fd_stderr, &spawn_error);
g_free(curr_dir);
g_strfreev(argvp);
#ifdef _WIN32
GIOChannel* channel_stdout = g_io_channel_win32_new_fd(fd_stdout);
GIOChannel* channel_stderr = g_io_channel_win32_new_fd(fd_stderr);
#else
GIOChannel* channel_stdout = g_io_channel_unix_new(fd_stdout);
GIOChannel* channel_stderr = g_io_channel_unix_new(fd_stderr);
#endif
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
GIOFlags flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
if (channel_stdout) {
g_io_channel_set_encoding(channel_stdout, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stdout, 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_buffer_size(channel_stderr, 10000);
}
g_main_loop_run(g_main_loop_new(NULL, false));
return 0;
}
+696
View File
@@ -0,0 +1,696 @@
/**************************************************************************
Copyright:
(C) 2008 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"
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)
{
debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n");
std::vector<std::string> devices;
std::string error_msg;
bool found = false;
// Try each one and move to next if it fails.
#if defined CONFIG_KERNEL_LINUX
if (!found) {
error_msg = detect_drives_linux_udev_byid(devices); // linux udev
// we check for devices vector emptiness because it could be a dummy directory
// with no files, so treat it as an error.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
if (!found) {
error_msg = detect_drives_linux_proc_partitions(devices); // linux /proc/partitions as fallback.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#elif defined CONFIG_KERNEL_WINDOWS
if (!found) {
error_msg = detect_drives_win32(devices); // win32
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#else // freebsd, etc...
if (!found) {
error_msg = detect_drives_dev(devices); // bsd, etc... . scans /dev.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#endif
if (!found) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n");
return error_msg; // last error message should be ok.
}
for (std::vector<std::string>::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) {
std::string dev = *iter;
// try to match against patterns
// for (unsigned int i = 0; i < match_patterns_.size(); i++) {
// try to match against general filter
// if (!app_pcre_match(match_patterns_[i], dev))
// continue;
// 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
blacked = true;
break;
}
}
StorageDeviceRefPtr drive(new StorageDevice(dev));
debug_out_info("app", "Found device: \"" << drive->get_device() << "\".\n");
if (!blacked) {
drives.push_back(drive);
// break; // no need to match other patters, go to next device
} else { // blacklisted
debug_out_info("app", "Device \"" << drive->get_device() << "\" is blacklisted, ignoring.\n");
// break; // go to next device
}
// }
}
debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n");
return std::string();
}
std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> smartctl_ex, 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
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() + "...");
// 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);
// normally we skip drives with errors - possibly scsi, etc...
if (return_first_error && !error_msg.empty())
return error_msg;
if (!error_msg.empty()) {
// use original executor error if present (permits matches by our users).
// if (!smartctl_ex->get_error_msg().empty())
// error_msg = smartctl_ex->get_error_msg();
fetch_data_errors_.push_back(error_msg);
fetch_data_error_outputs_.push_back(smartctl_ex->get_stdout_str());
}
debug_out_dump("app", "Device information for " << drive->get_device() << ":\n"
<< "\tModel: " << drive->get_model_name() << "\n"
<< "\tType: " << StorageDevice::get_type_name(drive->get_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n"
);
}
return std::string();
}
std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> smartctl_ex)
{
std::string error_msg = detect(put_drives_here);
if (error_msg.empty())
fetch_basic_data(put_drives_here, smartctl_ex, false); // ignore its errors, there may be plenty of them.
return error_msg;
}
@@ -1,18 +1,21 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include "storage_device.h"
#include "storage_detector.h"
#include "gsc_settings.h" // in src directory
int main()
{
// These settings contain device search paths, smartctl binary, etc...
init_default_settings();
std::vector<StorageDeviceRefPtr> drives;
// std::vector<std::string> match_patterns;
std::vector<std::string> blacklist_patterns; // additional parameters
@@ -29,7 +29,7 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSyn
std::string output;
// We don't use "--all" - it may cause really screwed up the output (tests, etc...).
// This looks just like "--info" only on non-smart devices.
std::string error_msg = execute_smartctl("--info --health --capabilities", smartctl_ex, output);
std::string error_msg = execute_smartctl("-i -H -c", smartctl_ex, output); // --info --health --capabilities
if (!error_msg.empty())
return error_msg;
@@ -52,9 +52,9 @@ std::string StorageDevice::parse_basic_data(bool emit_signal)
std::string version = SmartctlParser::parse_version(this->info_output_);
if (version.empty()) // is this smartctl data at all?
return "Cannot extract smartctl version information.";
return "Cannot get smartctl version information.";
// detect type. note: we can't distinguish between sata and scsi.
// detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_)) {
this->set_type(type_cddvd);
@@ -72,7 +72,8 @@ std::string StorageDevice::parse_basic_data(bool emit_signal)
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_)) { // usb flash drives output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds
|| app_pcre_match("/Device Read Identity Failed \\(not an ATA\\/ATAPI device\\)/mi", info_output_)) { // solaris scsi, unsupported by smartctl
smart_supported_ = false;
smart_enabled_ = false;
@@ -135,7 +136,7 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> sma
this->clear_fetched(); // clear everything fetched before, including outputs
std::string output;
std::string error_msg = execute_smartctl("--all", smartctl_ex, output);
std::string error_msg = execute_smartctl("-a", smartctl_ex, output); // --all
if (!error_msg.empty())
return error_msg;
@@ -210,7 +211,8 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
*/
std::string output;
std::string error_msg = execute_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output);
// --smart=on --saveauto=on, --smart=off
std::string error_msg = execute_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -245,7 +247,8 @@ SMART Automatic Offline Testing Disabled.
A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.
*/
std::string output;
std::string error_msg = execute_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output);
// --offlineauto=on, --offlineauto=off
std::string error_msg = execute_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -30,7 +30,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
// to display the correct icon
enum type_t {
type_unknown, // unknown. will be autodetected by smartctl
type_cddvd, // unsupported by smartctl
type_cddvd // unsupported by smartctl
// type_pata, // old pata
// we can't differentiate between these.
@@ -55,7 +55,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
status_enabled, // smart, aodc
status_disabled, // smart, aodc
status_unsupported, // smart, aodc
status_unknown, // aodc - supported but unknown if it's enabled or not.
status_unknown // aodc - supported but unknown if it's enabled or not.
};
static std::string get_status_name(status_t status, bool use_yesno = false)
@@ -45,7 +45,7 @@ class StorageAttribute {
enum attr_t {
attr_type_unknown,
attr_type_prefail, // reported: Pre-fail
attr_type_oldage, // reported: Old_age
attr_type_oldage // reported: Old_age
};
static std::string get_attr_type_name(attr_t a)
@@ -62,7 +62,7 @@ class StorageAttribute {
enum update_t {
update_type_unknown,
update_type_always, // reported: Always
update_type_offline, // reported: Offline
update_type_offline // reported: Offline
};
static std::string get_update_type_name(update_t a)
@@ -80,7 +80,7 @@ class StorageAttribute {
fail_time_unknown,
fail_time_none, // reported: -
fail_time_past, // reported: In_the_past
fail_time_now, // reported: FAILING_NOW
fail_time_now // reported: FAILING_NOW
};
static std::string get_fail_time_name(fail_time_t a)
@@ -245,7 +245,7 @@ class StorageProperty {
value_type_capability, // for "capabilities" subsection (non-time-interval blocks only)
value_type_attribute, // for "attributes" subsection
value_type_error_block, // for "error_log" subsection
value_type_selftest_entry, // for "selftest_log" subsection
value_type_selftest_entry // for "selftest_log" subsection
};
static std::string get_value_type_name(value_type_t type)
@@ -270,7 +270,7 @@ class StorageProperty {
section_unknown, // use when searching in all sections
section_info, // Short info (--info)
section_data, // SMART DATA
section_internal, // internal application-specific data
section_internal // internal application-specific data
};
static std::string get_section_name(section_t s)
@@ -293,7 +293,7 @@ class StorageProperty {
subsection_attributes, // Attributes (-A, --attributes). These need decoding.
subsection_error_log, // Error Log (-l error)
subsection_selftest_log, // Self-test log (-l selftest)
subsection_selective_selftest_log, // Selective self-test log and settings
subsection_selective_selftest_log // Selective self-test log and settings
};
static std::string get_subsection_name(subsection_t s)
@@ -315,7 +315,7 @@ class StorageProperty {
warning_none,
warning_notice, // a known attribute is somewhat disturbing, but no smart error
warning_warn, // smart warning is raised by old-age attribute
warning_alert, // smart warning is raised by pre-fail attribute, and similar errors
warning_alert // smart warning is raised by pre-fail attribute, and similar errors
};

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