Make sure that we are root before trying to run dmidecode.
[opensuse:pm-utils.git] / pm / pm-functions.in
1 #!/bin/sh
2 # vim:noexpandtab
3
4
5 # Default values go here.  It is important to _not_ initialize some
6 # variables here.  They are:
7 #
8 # PM_CMDLINE
9 # RESUME_MODULES
10 #
11 # for great debugging!
12 [ "${PM_DEBUG}" = "true" ] && {
13         export PM_DEBUG
14         set -x
15 }
16 set -a
17 PM_UTILS_LIBDIR="@PM-UTILS-LIBDIR@"
18 PM_UTILS_ETCDIR="@PM-UTILS-SYSCONFDIR@"
19 PM_UTILS_RUNDIR="/var/run/pm-utils"
20
21 PATH=/sbin:/usr/sbin:/bin:/usr/bin:"${PM_UTILS_LIBDIR}"/bin
22 PM_LOGFILE="/var/log/${STASHNAME}.log"
23 TEMPORARY_CPUFREQ_GOVERNOR="performance"
24 LOCKDIR="${PM_UTILS_RUNDIR}/locks"
25 STORAGEDIR="${PM_UTILS_RUNDIR}/${STASHNAME}/storage"
26 NA=254
27 NX=253
28 DX=252
29 PM_FUNCTIONS="$PM_UTILS_LIBDIR/functions"
30 PM_QUIRKDB="$PM_UTILS_LIBDIR/video-quirks"
31 PM_LKW_QUIRKS="$PM_UTILS_ETCDIR/last_known_working.quirkdb"
32 # Use c sort order
33 LC_COLLATE=C
34
35 # These should be overridden by defaults and/or config.d settings.
36 # Specifically, distros should override these by modifying defaults,
37 # and end users should modify these using files in /etc/pm/config.
38 HIBERNATE_MODE=""
39 HIBERNATE_RESUME_POST_VIDEO="no"
40 SLEEP_MODULE="auto"
41 # These variables will be handled specially when we load the config file.
42 SUSPEND_MODULES=""
43 HOOK_BLACKLIST=""
44 ADD_PARAMETERS=""
45 DROP_PARAMETERS=""
46 PARAMETERS="${STORAGEDIR}/parameters"
47 INHIBIT="${STORAGEDIR}/inhibit"
48 PM_CMDLINE="$*"
49 BEFORE_HOOKS=""
50 MODULE_HELP=""
51 SUSPEND_MODULE=""
52 HIBERNATE_MODULE=""
53 SUSPEND_HYBRID_MODULE=""
54
55 # variables to handle hibernate after suspend support
56 PM_HIBERNATE_DELAY=900  # 15 minutes
57 PM_RTC=/sys/class/rtc/rtc0
58
59 # when loading configuration files, allow stash-specific ones
60 # to override the pm-utils global ones.
61 [ -f "${PM_UTILS_LIBDIR}"/defaults ] && . "${PM_UTILS_LIBDIR}"/defaults
62 [ -f "${PM_UTILS_LIBDIR}/${STASHNAME}.defaults" ] && \
63     . "${PM_UTILS_LIBDIR}/${STASHNAME}.defaults"
64 set +a
65
66 for cfg in "${PM_UTILS_ETCDIR}"/config.d/*[!~] \
67            "${PM_UTILS_ETCDIR}/${STASHNAME}.config.d"/*[!~]; do
68         [ -f "$cfg" ] || continue
69         # Ugly, I know. The goal here is to allow multiple files in
70         # /etc/pm/config.d declare these variables and have those 
71         # declarations add together instead of the last one overwriting
72         # all the others.
73         [ "$SUSPEND_MODULES" ] && REAL_SUSPEND_MODULES="$SUSPEND_MODULES"
74         [ "$HOOK_BLACKLIST" ] && REAL_HOOK_BLACKLIST="$HOOK_BLACKLIST"
75         [ "$ADD_PARAMETERS" ] && REAL_ADD_PARAMETERS="$ADD_PARAMETERS"
76         [ "$DROP_PARAMETERS" ] && REAL_DROP_PARAMETERS="$DROP_PARAMETERS"
77         set -a
78         . "${cfg}"
79         SUSPEND_MODULES="$REAL_SUSPEND_MODULES $SUSPEND_MODULES"
80         HOOK_BLACKLIST="$REAL_HOOK_BLACKLIST $HOOK_BLACKLIST"
81         ADD_PARAMETERS="$REAL_ADD_PARAMETERS $ADD_PARAMETERS"
82         DROP_PARAMETERS="$REAL_DROP_PARAMETERS $DROP_PARAMETERS"
83         set +a
84 done
85
86 . "${PM_FUNCTIONS}"
87
88 if [ "$(id -u)" = "0" ] && command_exists dmidecode; then
89     export CHASSIS_TYPE="$(dmidecode -s chassis-type)"
90 else
91     export CHASSIS_TYPE=Unknown
92 fi
93
94 # Simple little logging function.
95 # We do it this way because 'echo -n' is not posix.
96 log()
97 {
98         is_set "$LOGGING" || return 0;
99         local fmt='%s\n'
100         [ "$1" = "-n" ] && { fmt='%s'; shift; }
101         printf "$fmt" "$*"
102 }
103
104 profiling() { [ "$PM_PROFILE" = "true" ]; }
105
106 if profiling; then
107         profile() {
108                 local t1 t2 status msg elapsed
109                 msg="$1"
110                 shift
111                 t1="$(date '+%s.%N')"
112                 "$@"
113                 status=$?
114                 t2="$(date '+%s.%N')"
115                 elapsed="$(printf "%s %s - p q" "$t2" "$t1" |dc)"
116                 log "$msg: $(printf "%.3f" $elapsed) ($t2 - $t1)"
117                 return $status
118         }
119
120 else
121         profile() { shift; "$@"; }
122 fi
123
124 add_before_hooks() {
125         [ -z "$BEFORE_HOOKS" ] && BEFORE_HOOKS="$*" || \
126                 BEFORE_HOOKS="$BEFORE_HOOKS $*"
127 }
128
129 add_module_help() {
130         [ -z "$MODULE_HELP" ] && MODULE_HELP="$*" || \
131                 MODULE_HELP="$MODULE_HELP $*"
132 }
133
134 before_hooks()
135 {
136         [ -z "$BEFORE_HOOKS" ] && return 0
137         local meth
138         for meth in $BEFORE_HOOKS; do
139                 command_exists "$meth" && "$meth"
140         done
141 }
142
143 sleep_module_help()
144 {
145         [ -z "$MODULE_HELP" ] && return 0
146         local meth
147         for meth in $MODULE_HELP; do
148                 command_exists "$meth" && "$meth"
149         done
150 }
151
152 # update PM_CMDLINE iff someone changed our parameters
153 update_parameters()
154 {
155         [ -f "$PARAMETERS.new" ] || return
156         export PM_CMDLINE="$(get_parameters)"
157         rm -f "$PARAMETERS.new"
158 }
159
160 # if the user asked us to blacklist any hooks, do it.
161 load_hook_blacklist()
162 {
163         [ "$HOOK_BLACKLIST" ] || return
164         local hook
165         for hook in $HOOK_BLACKLIST; do
166                 disablehook "${hook}" "blacklisted by user"
167                 log "Blacklisting ${hook}."
168         done
169 }
170
171 load_hook_parameters()
172 {
173         [ "$DROP_PARAMETERS" ] && remove_parameters $DROP_PARAMETERS
174         [ "$ADD_PARAMETERS" ]  && add_parameters $ADD_PARAMETERS
175         update_parameters
176 }
177
178
179 hook_exit_status(){
180         case $1 in
181                 0)   log "success." ;;
182                 $NA) log "not applicable." ;;
183                 $NX) log "not executable." ;;
184                 $DX) log "disabled." ;;
185                 *)   log "Returned exit code $1."; return 1 ;;
186         esac
187 }
188
189 # check to see if a hook is a candidate for being run.
190 hook_ok()
191 {
192         local hook="${1##*/}"
193         # the actual name as passed to us.
194         [ -f "$STORAGEDIR/disable_hook:$hook" ] && return $DX
195         # name with leading digits chopped off the filename
196         [ -f "$STORAGEDIR/disable_hook:${hook#[0-9][0-9]}" ] && return $DX
197         [ -x "$1" ] || return $NX
198         return 0
199 }
200
201 _run_hook() {
202         # $1 = hook to run
203         # rest of args passed to hook unchanged.
204         log -n "$*:"
205         hook_ok "$1" && "$@"
206         hook_exit_status $? && LAST_HOOK="${1##*/}" || inhibit
207 }
208
209 if profiling; then
210         run_hook() { profile "$1:" _run_hook "$@"; }
211 else
212         run_hook() { _run_hook "$@"; }
213 fi
214
215 # Run all applicable hooks, logging success and failure as we go.
216 _run_hooks() {
217         # $1 = type of hook to find.  
218         # $2 = paramaters to pass to hooks.
219         # $3 = if present and equal to "reverse", run hooks backwards.
220         # Currently only power and sleep are meaningful.
221         local syshooks="${PM_UTILS_ETCDIR}/$1.d"
222         local phooks="${PM_UTILS_LIBDIR}/$1.d"
223         command_exists before_hooks && before_hooks
224         local sort="sort"
225         local base
226         local hook
227         local oifs="${IFS}"
228         # the next two lines are not a typo or a formatting error!
229         local nifs="
230 "
231         IFS="${nifs}" # tolerate spaces in filenames.
232         [ "$3" = "reverse" ] && sort="sort -r"
233         for base in $(IFS="${oifs}"; for f in "$syshooks/"*[!~] "$phooks/"*[!~];
234                 do [ -O "$f" ] && echo ${f##*/} ; done | $sort | uniq) ;
235         do
236                 IFS="${oifs}"
237                 # if we are running backwards, skip hooks that we did not 
238                 # run going forwards due to a hook failing.
239                 [ "$3" -a "$3" = "reverse" -a "$LAST_HOOK" ] && \
240                     [ "$base" \> "$LAST_HOOK" ] && continue
241                 # if we have already inhibited suspend/resume,
242                 # don't run any more hooks.
243                 [ ! "$3" ] && inhibited && break
244                 update_parameters
245                 if [ -f "$syshooks/$base" ]; then
246                         hook="$syshooks/$base"
247                 elif [ -f "$phooks/$base" ]; then
248                         hook="$phooks/$base"
249                 fi
250                 run_hook "$hook" $2
251                 IFS="${nifs}"
252         done
253         IFS="${oifs}"
254         # return value is 1 if something was inhibited, 0 otherwise.
255         inhibited && return 1 || return 0
256 }
257
258 if profiling; then
259         run_hooks() { profile "$1 $2:" _run_hooks "$@"; }
260 else
261         run_hooks() { _run_hooks "$@"; }
262 fi
263
264 # Try to reinitalize the logfile. Fail unless certian criteria are met.
265 init_logfile()
266 {
267         if [ -z "$1" ]; then
268                 echo "Please pass a filename to init_logfile."
269                 return 1        
270         elif [ -h "$1" ]; then
271                 echo "$1 is a symbolic link, refusing to overwrite."
272                 return 1
273         elif [ -f "$1" -a ! -O "$1"  ]; then
274                 echo "We do not own $1, refusing to overwrite."
275                 return 1
276         fi
277         export LOGGING=true
278         exec > "$1" 2>&1
279 }
280
281 check_suspend() { [ -n "$SUSPEND_MODULE" ]; }
282 check_hibernate() { [ -n "$HIBERNATE_MODULE" ]; }
283 check_suspend_hybrid() { [ -n "$SUSPEND_HYBRID_MODULE" ]; }
284
285 # allow autodetection of sleep methods
286 if [ "$SLEEP_MODULE" = "auto" ]; then
287     SLEEP_MODULE="tuxonice uswsusp"
288 fi
289
290 for mod in $SLEEP_MODULE; do
291     mod="${PM_UTILS_LIBDIR}/module.d/${mod}"
292     [ -f "$mod" ] || continue
293     . "$mod"
294 done
295
296 # always fall back to kernel methods if nothing else was declared
297
298 if [ -z "$SUSPEND_MODULE" ]; then
299         if grep -q mem /sys/power/state; then
300                 SUSPEND_MODULE="kernel"
301                 do_suspend() { echo -n "mem" >/sys/power/state; }
302         elif [ -c /dev/pmu ] && pm-pmu --check; then
303                 SUSPEND_MODULE="kernel"
304                 do_suspend() { pm-pmu --suspend; }
305         elif grep -q standby /sys/power/state; then
306                 SUSPEND_MODULE="kernel"
307                 do_suspend() { echo -n "standby" >/sys/power/state; }
308         fi
309 fi
310
311 if [ -z "$HIBERNATE_MODULE" ] && \
312         [ -f /sys/power/disk ] && \
313         grep -q disk /sys/power/state; then
314         HIBERNATE_MODULE="kernel"
315         do_hibernate()
316         {
317                 [ -n "${HIBERNATE_MODE}" ] && \
318                 grep -qw "${HIBERNATE_MODE}" /sys/power/disk && \
319                 echo -n "${HIBERNATE_MODE}" > /sys/power/disk
320                 echo -n "disk" > /sys/power/state
321         }
322 fi
323
324 # since the kernel does not directly support hybrid sleep, we do
325 # something else -- suspend and schedule an alarm to go into
326 # hibernate if we have slept long enough.
327 # Only do this if we do not need to do any special video hackery on resume
328 # from hibernate, though.
329 if [ -z "$SUSPEND_HYBRID_MODULE" -a -w "$PM_RTC/wakealarm" ] && \
330     check_suspend && check_hibernate && ! is_set $HIBERNATE_RESUME_POST_VIDEO; \
331     then
332     SUSPEND_HYBRID_MODULE="kernel"
333     do_suspend_hybrid() {
334         WAKETIME=$(( $(cat "$PM_RTC/since_epoch") + PM_HIBERNATE_DELAY))
335         echo >"$PM_RTC/wakealarm"
336         echo $WAKETIME > "$PM_RTC/wakealarm"
337         if do_suspend; then
338             NOW=$(cat "$PM_RTC/since_epoch")
339             if [ "$NOW" -ge "$WAKETIME" -a "$NOW" -lt $((WAKETIME + 30)) ]; then
340                 log "Woken by RTC alarm, hibernating."
341                 # if hibernate fails for any reason, go back to suspend.
342                 do_hibernate || do_suspend
343             else
344                 echo > "$PM_RTC/wakealarm"
345             fi
346         else
347             # if we cannot suspend, just try to hibernate.
348             do_hibernate
349         fi
350     }
351 fi