]> sourceware.org Git - systemtap.git/blob - stap-client
Consolidate task_finder/vma tracker initialization.
[systemtap.git] / stap-client
1 #!/bin/bash
2
3 # Compile server client for systemtap
4 #
5 # Copyright (C) 2008-2010 Red Hat Inc.
6 #
7 # This file is part of systemtap, and is free software. You can
8 # redistribute it and/or modify it under the terms of the GNU General
9 # Public License (GPL); either version 2, or (at your option) any
10 # later version.
11
12 # This script examines the systemtap command line and packages the files and
13 # information needed to execute the command. This is then sent to a trusted
14 # systemtap server which will process the request and return the resulting
15 # kernel module (if requested) and any other information generated by the
16 # request. If a kernel module is generated, this script will load the module
17 # and execute it using 'staprun', if requested.
18
19 # Catch ctrl-c and other termination signals
20 trap 'terminate' SIGTERM
21 trap 'interrupt' SIGINT
22 trap 'ignore_signal' SIGHUP SIGPIPE
23
24 # Initialize the environment
25 . ${PKGLIBEXECDIR}stap-env
26
27 #-----------------------------------------------------------------------------
28 # Helper functions.
29 #-----------------------------------------------------------------------------
30 # function: initialization
31 function initialization {
32 our_host_name=`expr "$HOSTNAME" : "\\\([a-zA-Z0-9-]*\\\).*"`
33 our_domain_name=`expr "$HOSTNAME" : "$our_host_name\\\(.*\\\)"`
34
35 rc=0
36 wd=`pwd`
37 umask 0
38
39 # Default location for server certificates if we're not root
40 # Must be owned by us.
41 local uid
42 if test $EUID != 0; then
43 if test -e $stap_user_ssl_db/client; then
44 if check_db $stap_user_ssl_db/client $EUID $USER; then
45 local_ssl_dbs=$stap_user_ssl_db/client
46 fi
47 fi
48 fi
49 # Additional location for all users. Must be owned by root.
50 if test -e $stap_root_ssl_db/client; then
51 if check_db $stap_root_ssl_db/client 0 root; then
52 public_ssl_dbs=$stap_root_ssl_db/client
53 fi
54 fi
55
56 # Default options settings
57 p_phase=5
58 v_level=0
59 keep_temps=0
60 m_name=
61 module_name=stap_$$
62 uname_r="`uname -r`"
63 arch=`stap_get_arch`
64
65 # Default variable settings
66 find_all=
67
68 # Create a temporary directory to package things in
69 # Do this before parsing the command line so that there is a place
70 # to put -I and -R directories.
71 tmpdir_client=`mktemp -dt $stap_tmpdir_prefix_client.XXXXXX` || \
72 fatal "Cannot create temporary directory " $tmpdir_client
73 tmpdir_env=`dirname $tmpdir_client`
74
75 session_only_prompt_done=0
76 }
77
78 # function: parse_options [ STAP-OPTIONS ]
79 #
80 # Examine the command line. We need not do much checking, but we do need to
81 # parse all options in order to discover the ones we're interested in.
82 # The server will take care of most situations and return the appropriate
83 # output.
84 #
85 function parse_options {
86 # Each command line argument will be written to its own file within the
87 # request package.
88 argc=1
89 packed_options='-'
90 arg_subst=
91
92 while test $# != 0
93 do
94 advance=0
95 dash_seen=0
96 client_arg=0
97
98 # Start of a new token.
99 first_token="$1"
100 until test $advance != 0
101 do
102 # Identify the next option
103 first_char="${first_token:0:1}"
104 second_char=
105 if test $dash_seen = 0; then
106 if test "$first_char" = "-"; then
107 if test "$first_token" != "-"; then
108 # It's not a lone dash, so it's an option.
109 # Is it a long option (i.e. --option)?
110 second_char="${first_token:1:1}"
111 if test "X$second_char" = "X-"; then
112 case "$first_token" in
113 --ssl=*)
114 process_ssl "$first_token"
115 ;;
116 --server=*)
117 process_server "$first_token"
118 ;;
119 *)
120 # An unknown or unimportant option. Ignore it.
121 ;;
122 esac
123 advance=$(($advance + 1))
124 break
125 fi
126 # It's not a lone dash, or a long option, so it's a short option string.
127 # Remove the dash.
128 first_token="${first_token:1}"
129 dash_seen=1
130 first_char="${first_token:0:1}"
131 fi
132 fi
133 if test $dash_seen = 0; then
134 # The dash has not been seen. This is either the script file
135 # name or an argument to be passed to the probe module.
136 # If this is the first time, and -e has not been specified,
137 # then it could be the name of the script file.
138 if test "X$e_script" = "X" -a "X$script_file" = "X"; then
139 script_file="$first_token"
140 script_file_argc=$argc
141 fi
142 advance=$(($advance + 1))
143 break
144 fi
145 fi
146
147 # We are at the start of an option. Look at the first character.
148 case $first_char in
149 a)
150 get_arg "$first_token" "$2"
151 process_a "$stap_arg"
152 ;;
153 B)
154 get_arg "$first_token" "$2"
155 ;;
156 c)
157 get_arg "$first_token" "$2"
158 process_c "$stap_arg"
159 ;;
160 D)
161 get_arg "$first_token" "$2"
162 ;;
163 e)
164 get_arg "$first_token" "$2"
165 process_e "$stap_arg"
166 ;;
167 I)
168 get_arg "$first_token" "$2"
169 process_I "$stap_arg"
170 ;;
171 k)
172 keep_temps=1
173 ;;
174 l)
175 get_arg "$first_token" "$2"
176 p_phase=2
177 ;;
178 L)
179 get_arg "$first_token" "$2"
180 p_phase=2
181 ;;
182 m)
183 get_arg "$first_token" "$2"
184 process_m "$stap_arg"
185 ;;
186 o)
187 get_arg "$first_token" "$2"
188 process_o "$stap_arg"
189 ;;
190 p)
191 get_arg "$first_token" "$2"
192 process_p "$stap_arg"
193 ;;
194 r)
195 get_arg "$first_token" "$2"
196 process_r "$stap_arg"
197 ;;
198 R)
199 get_arg "$first_token" "$2"
200 process_R "$stap_arg"
201 ;;
202 s)
203 get_arg "$first_token" "$2"
204 ;;
205 S)
206 get_arg "$first_token" "$2"
207 ;;
208 v)
209 v_level=$(($v_level + 1))
210 ;;
211 x)
212 get_arg "$first_token" "$2"
213 ;;
214 *)
215 # An unknown or unimportant flag.
216 ;;
217 esac
218
219 if test $advance = 0; then
220 # Just another flag character. Consume it.
221 first_token="${first_token:1}"
222 if test "X$first_token" = "X"; then
223 advance=$(($advance + 1))
224 fi
225 fi
226 done
227
228 # Consume the arguments we just processed.
229 while test $advance != 0; do
230 # Does the final argument file contain a client-side file
231 # name which must be changed to a server-side name?
232 local arg
233 if test "X$arg_subst" != "X" -a $advance = 1; then
234 arg="$arg_subst"
235 arg_subst=
236 else
237 arg="$1"
238 fi
239
240 # If it's not client-only argument,
241 # place the argument in a numbered file within our temp
242 # directory.
243 # o We don't write a newline at the end, since newline could be
244 # part of the argument.
245 # o We add an X to the beginning of the file
246 # in order to avoid having 'echo' interpret the output as
247 # its own option. We then remove the X.
248 # There must be a better way.
249 if test $client_arg = 0; then
250 echo -n "X$arg" > "$tmpdir_client/argv$argc"
251 sed -i "s|^X||" "$tmpdir_client/argv$argc"
252 argc=$(($argc + 1))
253 fi
254
255 # Get the next argument.
256 shift
257 advance=$(($advance - 1))
258 packed_options='-'
259 done
260 done
261
262 # If the script file was given and it's not '-', then replace it with its
263 # client-temp-name in its argument file.
264 if test "X$script_file" != "X"; then
265 local local_name
266 if test "$script_file" != "-"; then
267 generate_client_temp_name "$script_file"
268 local_name="$client_temp_name"
269 else
270 local_name="-"
271 fi
272 echo -n "script/$local_name" > "$tmpdir_client/argv$script_file_argc"
273 fi
274
275 # Processing based on final options settings
276 # Complete the list of local certificate databases
277 local_ssl_dbs="$additional_local_ssl_dbs $local_ssl_dbs"
278
279 # We must have at least one usable certificate database.
280 test "X$local_ssl_dbs" != "X " -o "X$public_ssl_dbs" != "X" || \
281 fatal "No usable certificate databases found"
282 }
283
284 # function: get_arg FIRSTWORD SECONDWORD
285 #
286 # Collect an argument to the given option
287 function get_arg {
288 # Remove first character.
289 local opt="${1:0:1}"
290 local first="${1:1}"
291 packed_options="${packed_options}$opt"
292
293 # Advance to the next token, if the first one is exhausted.
294 if test "X$first" = "X"; then
295 advance=$(($advance + 1))
296 first="$2"
297 fi
298
299 stap_arg="$first"
300 test "X$first" != "X" && advance=$(($advance + 1))
301 }
302
303 # function: process_ssl ARGUMENT
304 #
305 # Process the --ssl option.
306 function process_ssl {
307 client_arg=1
308 local db="${1:6}"
309
310 test "X$db" != "X" || \
311 fatal "Missing argument to --ssl"
312
313 check_db "$db" || return
314
315 additional_local_ssl_dbs="$additional_local_ssl_dbs $db"
316 }
317
318 # function: process_server ARGUMENT
319 #
320 # Process the --server option.
321 function process_server {
322 client_arg=1
323 local spec="${1:9}"
324
325 test "X$spec" != "X" || \
326 fatal "Missing argument to --server"
327
328 specified_servers="$specified_servers $spec"
329 }
330
331 # function: process_c ARGUMENT
332 #
333 # Process the -c flag.
334 function process_c {
335 c_cmd="$1"
336 }
337
338 # function: process_e ARGUMENT
339 #
340 # Process the -e flag.
341 function process_e {
342 # Only the first -e option is recognized and it overrides any script file name
343 # which may have already been identified.
344 if test "X$e_script" = "X"; then
345 e_script="$1"
346 script_file=
347 fi
348 }
349
350 # function: process_I ARGUMENT ORIGINAL_ARGUMENT
351 #
352 # Process the -I flag.
353 function process_I {
354 test "X$1" = "X" && return
355 test "${1:0:1}" = "
356 " && return
357 include_file_or_directory tapsets "$1"
358 if test $advance = 1; then
359 arg_subst="${packed_options}tapsets/$included_name"
360 else
361 arg_subst="tapsets/$included_name"
362 fi
363 }
364
365 # function: process_m ARGUMENT
366 #
367 # Process the -m flag.
368 function process_m {
369 module_name="$1"
370 m_name="$1"
371 }
372
373 # function: process_o ARGUMENT
374 #
375 # Process the -o flag.
376 function process_o {
377 stdout_redirection="$1"
378 }
379
380 # function: process_p ARGUMENT
381 #
382 # Process the -p flag.
383 function process_p {
384 p_phase="$1"
385 }
386
387 # function: process_r ARGUMENT
388 #
389 # Process the -r flag.
390 function process_r {
391 local first_char="${1:0:1}"
392
393 if test "$first_char" = "/"; then # fully specified path
394 kernel_build_tree="$1"
395 version_file_name="$kernel_build_tree/include/config/kernel.release"
396 # The file include/config/kernel.release within the
397 # build tree is used to pull out the version information
398 release=`cat "$version_file_name" 2>/dev/null`
399 if test "X$release" = "X"; then
400 fatal "Missing $version_file_name"
401 return
402 fi
403 else
404 # kernel release specified directly
405 release="$1"
406 fi
407
408 if test "X$release" != "X$uname_r"; then
409 uname_r="$release"
410 find_all="--all"
411 fi
412 }
413
414 # function: process_a ARGUMENT
415 #
416 # Process the -a flag.
417 function process_a {
418 if test "X$1" != "X$arch"; then
419 arch="$1"
420 find_all="--all"
421 fi
422 }
423
424 # function: process_R ARGUMENT ORIGINAL_ARGUMENT
425 #
426 # Process the -R flag.
427 function process_R {
428 test "X$1" = "X" && return
429 test "${1:0:1}" = "
430 " && return
431 include_file_or_directory runtime "$1"
432 if test $advance = 1; then
433 arg_subst="${packed_options}runtime/$included_name"
434 else
435 arg_subst="runtime/$included_name"
436 fi
437 }
438
439 # function: include_file_or_directory PREFIX NAME
440 #
441 # Include the given file or directory in the client's temporary
442 # tree to be sent to the server and save it's name in the variable
443 # included_name. We use a global variable instread of echoing the
444 # result since the use of `include_file_or_directory` loses a trailing
445 # newline.
446 function include_file_or_directory {
447 # Add a symbolic link of the named file or directory to our temporary
448 # directory, but only if the file or directory exists.
449 generate_client_temp_name "$2"
450 local local_name="$client_temp_name"
451 included_name="$local_name"
452 test -e "/$local_name" || return
453
454 local local_dirname=`dirname "$local_name"`
455 mkdir -p "$tmpdir_client/$1/$local_dirname" || \
456 fatal "Could not create $tmpdir_client/$1/$local_dirname"
457 ln -s "/$local_name" "$tmpdir_client/$1/$local_name" || \
458 fatal "Could not link $tmpdir_client/$1/$local_name to /$local_name"
459 }
460
461 # function: generate_client_temp_name NAME
462 #
463 # Generate the name to be used for the given file/directory relative to the
464 # client's temporary directory and stores it in the variable
465 # client_temp_name. We use a global variable instread of echoing the
466 # result since the use of `generate_client_temp_name` loses a trailing
467 # newline.
468 function generate_client_temp_name {
469 # Transform the name into a fully qualified path name
470 local full_name="$1"
471 test "${full_name:0:1}" != "/" && full_name="$wd/$full_name"
472
473 # The same name without the initial / or trailing /
474 local local_name="${full_name:1}"
475 test "${local_name: -1:1}" = "/" && local_name="${local_name:0:$((${#local_name}-1))}"
476
477 client_temp_name="$local_name"
478 }
479
480 # function: create_request
481 #
482 # Add information to the client's temp directory representing the request
483 # to the server.
484 function create_request {
485 # Work in our temporary directory
486 cd $tmpdir_client
487
488 if test "X$script_file" != "X"; then
489 if test "$script_file" = "-"; then
490 mkdir -p $tmpdir_client/script || \
491 fatal "Cannot create temporary directory " $tmpdir_client/script
492 cat > "$tmpdir_client/script/$script_file"
493 else
494 include_file_or_directory script "$script_file"
495 fi
496 fi
497
498 # Add the necessary info to special files in our temporary directory.
499 echo "sysinfo: `client_sysinfo`" > sysinfo
500 }
501
502 # function client_sysinfo
503 #
504 # Generate the client's sysinfo and echo it to stdout
505 function client_sysinfo {
506 echo "$uname_r $arch"
507 }
508
509 # function: package_request
510 #
511 # Package the client's temp directory into a form suitable for sending to the
512 # server.
513 function package_request {
514 # Package up the temporary directory into a zip file
515 cd $tmpdir_env
516
517 local tmpdir_client_base=`basename $tmpdir_client`
518 zip_client=$tmpdir_env/`mktemp $tmpdir_client_base.zip.XXXXXX` || \
519 fatal "Cannot create temporary file " $zip_client
520
521 cd $tmpdir_client
522 (rm -f $zip_client && zip -r $zip_client * > /dev/null) || \
523 fatal "zip of request tree, $tmpdir_client, failed"
524 }
525
526 # function: unpack_response
527 #
528 # Unpack the zip file received from the server and make the contents available
529 # for printing the results and/or running 'staprun'.
530 function unpack_response {
531 tmpdir_server=`mktemp -dt $stap_tmpdir_prefix_client.server.XXXXXX` || \
532 fatal "Cannot create temporary file " $tmpdir_server
533
534 # Unpack the server output directory
535 unzip -d $tmpdir_server $zip_server > /dev/null || \
536 fatal "Cannot unpack server response, $zip_server"
537
538 # Check the contents of the directory. It should contain:
539 # 1) a file called stdout
540 # 2) a file called stderr
541 # 3) a file called rc
542 # 4) optionally a directory named to match stap??????
543 local num_files=`ls $tmpdir_server | wc -l`
544 test $num_files = 4 -o $num_files = 3 || \
545 fatal "Wrong number of files in server's temp directory"
546 test -f $tmpdir_server/stdout || \
547 fatal "`pwd`/$tmpdir_server/stdout does not exist or is not a regular file"
548 test -f $tmpdir_server/stderr || \
549 fatal "`pwd`/$tmpdir_server/stderr does not exist or is not a regular file"
550 test -f $tmpdir_server/rc || \
551 fatal "`pwd`/$tmpdir_server/rc does not exist or is not a regular file"
552
553 # See if there is a systemtap temp directory. There should be at least an empty one.
554 # ls -l $tmpdir_server
555 tmpdir_stap=`cd $tmpdir_server && ls | grep stap......\$ 2>/dev/null`
556 if test "X$tmpdir_stap" != "X"; then
557 test -d $tmpdir_server/$tmpdir_stap || \
558 fatal "$tmpdir_server/$tmpdir_stap is not a directory"
559
560 # Move the systemtap temp directory to a local temp location, if -k
561 # was specified.
562 if test $keep_temps = 1; then
563 local local_tmpdir_stap=`mktemp -dt stapXXXXXX` || \
564 fatal "Cannot create temporary directory " $local_tmpdir_stap
565 mv $tmpdir_server/$tmpdir_stap/* $local_tmpdir_stap 2>/dev/null
566 rm -fr $tmpdir_server/$tmpdir_stap
567
568 # Correct the name of the temp directory in the server's stderr output
569 sed -i "s,^Keeping temporary directory.*,Keeping temporary directory \"$local_tmpdir_stap\"," $tmpdir_server/stderr
570 tmpdir_stap=$local_tmpdir_stap
571 else
572 tmpdir_stap=$tmpdir_server/$tmpdir_stap
573 # Make sure we own the systemtap temp directory if we are root.
574 test $EUID = 0 && chown $EUID:$EUID $tmpdir_stap
575 fi
576 fi
577
578 if test $keep_temps = 0; then
579 # Remove the output line due to the synthetic server-side -k
580 sed -i "/^Keeping temporary directory.*/ d" $tmpdir_server/stderr
581 fi
582
583 if test $p_phase = 5; then
584 # Remove the output line due to the synthetic server-side -p4
585 sed -i "/^.*\.ko$/ d" $tmpdir_server/stdout
586 fi
587 }
588
589 # function: find_and_connect_to_server
590 #
591 # Find and establish connection with a compatible stap server.
592 function find_and_connect_to_server {
593 local num_servers=0
594
595 # Make a place to receive the response file.
596 zip_server=`mktemp -t $stap_tmpdir_prefix_client.server.zip.XXXXXX` || \
597 fatal "Cannot create temporary file " $zip_server
598
599 # If servers were specified on the command line, then try them
600 # in sequence. Don't try any other servers.
601 if test "X$specified_servers" != "X"; then
602 for server in $specified_servers; do
603 num_servers=$(($num_servers + 1))
604
605 # If the server is completely specified, (i.e. server:port),
606 # then try it directly.
607 port=`expr "$server" : '.\+:\([0-9]\+\)'`
608 if test "X$port" != "X"; then
609 name=`expr "$server" : '\(.\+\):[0-9]\+'`
610
611 # If we have been given an ip address, then try to resolve it to a name.
612 # If we have been given a name, try to resolve the full name.
613 # The full name is needed in order to validate the server's certificate.
614 address=`expr "$name" : '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)'`
615 if test "X$address" = "X"; then
616 # We've been given a host name
617 full_name=`nslookup $name | awk '/^Name\:/ {print $2}'`
618 if test "X$full_name" != "X"; then
619 name=$full_name
620 fi
621 else
622 # We've been given an ip address.
623 name=`nslookup $address | awk '/in-addr\.arpa/ {print $4}'`
624 name=`expr "$name" : '\(.*\)\.'`
625 if test "X$name" = "X"; then
626 echo "Cannot resolve ip address $address" >> "$tmpdir_client/connect"
627 continue
628 fi
629 fi
630
631 # Now try to contact the given server.
632 send_receive $name $port && return
633 continue
634 fi
635
636 # Otherwise select the matching server from the available servers
637 # and use the port it is advertizing.
638 #
639 # Have we been given an ip address? If so, just use it.
640 address=`expr "$server" : '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)'`
641 if test "X$address" = "X"; then
642 # We have not been given an ip address. Try to resolve it as a host name.
643 if test "X$server" = "Xlocalhost"; then
644 # We don't want the address of the loopback interface here. Avahi will present
645 # the actual ip address.
646 server=$our_host_name$our_domain_name
647 fi
648 address=`nslookup $server | awk '/^Address\:[ \t][0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {print $2}'`
649 if test "X$address" = "X"; then
650 echo "Cannot resolve server $server" >> "$tmpdir_client/connect"
651 continue
652 fi
653 fi
654
655 ${stap_pkglibexecdir}stap-find-servers $find_all | grep $address > "$tmpdir_client/servers"
656
657 if test `wc -l "$tmpdir_client/servers" | awk '{print $1}'` = "0"; then
658 warning "No server is available on $server" 2>> "$tmpdir_client/connect"
659 continue
660 fi
661
662 choose_server && return
663 done
664 else
665 # No servers specified. Find available servers and choose one of them.
666 # Remember which ssl certificate database was used to authenticate the chosen
667 # server.
668 ${stap_pkglibexecdir}stap-find-servers $find_all > "$tmpdir_client/servers"
669 choose_server && return
670 num_servers=`wc -l "$tmpdir_client/servers" | awk '{print $1}'`
671 fi
672
673 if test $num_servers = 0; then
674 fatal "Unable to find a server"
675 fi
676
677 test -f "$tmpdir_client/connect" && cat "$tmpdir_client/connect" >&2
678 fatal "Unable to connect to a server"
679 }
680
681 # function: choose_server
682 #
683 # Examine each line from "$tmpdir_client/servers" and attempt to connect to
684 # each server specified until successful.
685 function choose_server {
686 local name ip port remain
687
688 while read -u3 name ip port remain
689 do
690 if test "X$name" = "X"; then
691 fatal "Server name not provided by avahi"
692 fi
693
694 # if test "X$ip" = "X"; then
695 # fatal "Server ip address not provided by avahi"
696 # fi
697
698 if test "X$port" = "X"; then
699 fatal "Server port not provided by avahi"
700 fi
701
702 # Does the server build for the kernel release and architecture that we want?
703 sysinfo=`expr "$remain" : "'sysinfo=\\\(.*\\\)'"`
704 test "X$sysinfo" != "X$uname_r $arch" && continue
705
706 send_receive $name $port && return
707 done 3< "$tmpdir_client/servers"
708
709 # Could not connect to a server
710 return 1
711 }
712
713 # function: send_receive SERVER PORT
714 #
715 # Connect to the server, send the request and receive the response.
716 function send_receive {
717 local server="$1"
718 local port="$2"
719
720 # The server must match the dns name on the certificate
721 # and must be 'localhost' if the server is on the local host.
722 local server_host_name=`expr "$server" : "\\\([a-zA-Z0-9-]*\\\).*"`
723 local server_domain_name=`expr "$server" : "$server_host_name\\\(.*\\\)"`
724
725 if test "X$server_domain_name" = "X.local"; then
726 server_domain_name=$our_domain_name
727 fi
728 if test "X$server_host_name$server_domain_name" = "Xlocalhost$our_domain_name"; then
729 server=localhost
730 elif test "X$server_host_name$server_domain_name" = "X$our_host_name$our_domain_name"; then
731 server=localhost
732 else
733 server=$server_host_name$server_domain_name
734 fi
735
736 # Try to connect using each of the given local certificate databases in turn
737 # for verification.
738 local rc
739 for ssl_db in $local_ssl_dbs
740 do
741 # Send the request and receive the response using stap-client-connect
742 attempt_connection -i $zip_client -o $zip_server -d $ssl_db -p $port -h $server && return
743
744 # Try the next database, but give the server a chance to reset.
745 sleep 1
746 done
747
748 # Next, try the public certificate databases.
749 for ssl_db in $public_ssl_dbs
750 do
751 # Send the request and receive the response using stap-client-connect
752 attempt_connection -i $zip_client -o $zip_server -d $ssl_db -p $port -h $server && return
753
754 # Try the next database, but give the server a chance to reset.
755 sleep 1
756 done
757
758 # Could not connect using any of the certificate databases
759 return 1
760 }
761
762 # function: attempt_connection ARGS
763 #
764 # Attempt connection with the given server. Give the user a chance to
765 # trust the server if it is not already trusted
766 function attempt_connection {
767 echo "Attempting connection with $server:$port using certificate database in '$ssl_db'" >> "$tmpdir_client/connect"
768
769 # Send the request and receive the response using stap-client-connect
770 ${stap_pkglibexecdir}stap-client-connect "$@" >> "$tmpdir_client/connect" 2>&1 &
771 wait '%${stap_pkglibexecdir}stap-client-connect'
772 local rc=$?
773 test $rc = 0 && return
774
775 # The connection failed. If it was because the server is not trusted, give
776 # the user a chance to decide whether to trust the server anyway.
777 # The prompt will not be printed and the read will quickly timeout if
778 # stdin is not from a terminal.
779 if test $rc = 2; then
780 # Output any connection messages generated thus far
781 cat "$tmpdir_client/connect" >&2
782 rm "$tmpdir_client/connect"
783
784 local response
785 local prompt="The server at $server:$port is not trusted based on the certificate database in '$ssl_db'
786 "
787 if test $session_only_prompt_done = 0; then
788 session_only_prompt_done=1
789 prompt="${prompt}Trust this server for for this session only? [y/N] "
790 read -t 30 -p "$prompt" response || echo n
791 if test "$response" = "y" -o "$response" = "Y"; then
792 ${stap_pkglibexecdir}stap-client-connect "$@" -t session >> "$tmpdir_client/connect" 2>&1 &
793 wait '%${stap_pkglibexecdir}stap-client-connect'
794 test $? = 0 && return
795 return 1 # Connection failed
796 fi
797 prompt=
798 fi
799 if test "$ssl_db" = "$stap_root_ssl_db/client"; then
800 prompt="${prompt}Adding this server's certificate to this database will make this server trusted by all users on the local host.
801 "
802 fi
803 prompt="${prompt}Add this server's certificate to '$ssl_db'? [y/N] "
804 read -t 30 -p "$prompt" response || echo n
805 if test "$response" = "y" -o "$response" = "Y"; then
806 ${stap_pkglibexecdir}stap-client-connect "$@" -t permanent >> "$tmpdir_client/connect" 2>&1 &
807 wait '%${stap_pkglibexecdir}stap-client-connect'
808 test $? = 0 && return
809 fi
810 fi
811
812 # Connection failed
813 return 1
814 }
815
816 # function: process_response
817 #
818 # Write the stdout and stderr from the server to stdout and stderr respectively.
819 function process_response {
820 # Pick up the results of running stap on the server.
821 cd $tmpdir_server
822 rc=`cat rc`
823
824 if test $p_phase -ge 4; then
825 if test -f $tmpdir_stap/*.ko; then
826 if test $p_phase = 4 -o "X$m_name" != "X"; then
827 cp -p $tmpdir_stap/*.ko $wd/$module_name.ko
828 test -f $tmpdir_stap/*.sgn && cp -p $tmpdir_stap/*.sgn $wd/$module_name.ko.sgn
829 else
830 module_name=`ls $tmpdir_stap/*.ko`
831 module_name=`expr "$module_name" : '\(.*\)\.ko'`
832 fi
833 elif test "X$script_file" != "X" -o "X$e_script" != "X"; then
834 if test "X$rc" != "X" -a $rc = 0; then
835 stream_output
836 fatal "no module returned by the server"
837 fi
838 fi
839 fi
840
841 # Change the name of the temp directory and module name in stdout and stderr
842 sed -i "s,stap_[0-9]\+,$module_name,g" $tmpdir_server/stdout
843 sed -i "s,stap_[0-9]\+,$module_name,g" $tmpdir_server/stderr
844 sed -i "s,into \".*$module_name,into \"$module_name,g" $tmpdir_server/stdout
845 sed -i "s,into \".*$module_name,into \"$module_name,g" $tmpdir_server/stderr
846
847 # Output stdout and stderr as directed
848 stream_output
849 }
850
851 # function: stream_output
852 #
853 # Output stdout and stderr as directed
854 function stream_output {
855 cd $tmpdir_server
856 cat stderr >&2
857 cat stdout
858 }
859
860 # function: maybe_call_staprun
861 #
862 # Call staprun using the module returned from the server, if requested.
863 function maybe_call_staprun {
864 if test $rc != 0; then
865 # stap run on the server failed, so don't bother
866 return
867 fi
868
869 if test $p_phase -ge 4; then
870 # There should be a systemtap temporary directory.
871 if test "X$tmpdir_stap" = "X"; then
872 # OK if no script specified
873 if test "X$e_script" != "X" -o "X$script_file" != "X"; then
874 fatal "systemtap temporary directory is missing in server response"
875 fi
876 return
877 fi
878
879 if test $p_phase = 5; then
880 test $v_level -gt 0 && echo "Pass 5: starting run." >&2
881
882 # We have a module. Try to run it
883 # If a -c command was specified, pass it along.
884 if test "X$c_cmd" != "X"; then
885 staprun_opts="-c '$c_cmd'"
886 fi
887
888 # The -v level will be one less than what was specified
889 # for us.
890 for ((vl=$((v_level - 1)); $vl > 0; --vl))
891 do
892 staprun_opts="$staprun_opts -v"
893 done
894
895 # if -o was specified, pass it along
896 if test "X$stdout_redirection" != "X"; then
897 staprun_opts="$staprun_opts -o $stdout_redirection"
898 fi
899
900 # Run it from our original working directory
901 cd $wd
902
903 # Run it in the background and wait for it. This
904 # way any signals sent to us can be caught.
905 if test $v_level -ge 2; then
906 echo "running `staprun_PATH` $staprun_opts $module_name.ko" >&2
907 fi
908 eval `staprun_PATH` "$staprun_opts" $module_name.ko
909 rc=$?
910
911 # Wait until the job actually disappears so that its output is complete.
912 while jobs '%?staprun' >/dev/null 2>&1
913 do
914 sleep 1
915 done
916
917 test $v_level -gt 0 && echo "Pass 5: run completed in 0usr/0sys/0real ms." >&2
918 fi
919 fi
920 }
921
922 # function: staprun_PATH
923 #
924 # Compute a PATH suitable for running staprun.
925 function staprun_PATH {
926 # If $SYSTEMTAP_STAPRUN is set, then use that
927 if test "X$SYSTEMTAP_STAPRUN" != "X"; then
928 echo $SYSTEMTAP_STAPRUN
929 return
930 fi
931
932 # Otherwise, if there is an exec_prefix, then use it.
933 if test "X$stap_exec_prefix" != "X"; then
934 echo ${stap_exec_prefix}staprun
935 return
936 fi
937
938 # Otherwise, we have been called by the dejagnu test harness as 'stap'
939 # and we are the first 'stap' on the path. Since staprun may call
940 # 'stap', remove the PATH component where we live from the PATH in order to
941 # avoid recursion.
942 local first_stap=`which stap`
943 local PATH_component=`dirname $first_stap`
944 echo "PATH=$PATH staprun" | sed "s,$PATH_component,,g"
945 }
946
947 # function: check_db DBNAME [ EUID USER ]
948 #
949 # Check the security of the given database directory.
950 function check_db {
951 local dir="$1"
952 local euid="$2"
953 local user="$3"
954 local rc=0
955
956 # Check that we have been given a directory
957 if ! test -e $dir; then
958 warning "Certificate database '$dir' does not exist"
959 return 1
960 fi
961 if ! test -d $dir; then
962 warning "Certificate database '$dir' is not a directory"
963 return 1
964 fi
965
966 # If euid has been specified, then this directory must be owned by that
967 # user.
968 if test "X$euid" != "X"; then
969 local ownerid=`stat -c "%u" $dir`
970 if test "X$ownerid" != "X$euid"; then
971 warning "Certificate database '$dir' must be owned by $user"
972 rc=1
973 fi
974 fi
975
976 # Check that we can read the directory
977 if ! test -r $dir; then
978 warning "Certificate database '$dir' is not readble"
979 rc=1
980 fi
981
982 # Check the access permissions of the directory
983 local perm=0`stat -c "%a" $dir`
984 if test $((($perm & 0400) == 0400)) = 0; then
985 warning "Certificate database '$dir' should be readable by the owner"
986 fi
987 if test $((($perm & 0200) == 0200)) = 0; then
988 warning "Certificate database '$dir' should be writeable by the owner"
989 fi
990 if test $((($perm & 0100) == 0100)) = 0; then
991 warning "Certificate database '$dir' should be searchable by the owner"
992 fi
993 if test $((($perm & 0040) == 0040)) = 0; then
994 warning "Certificate database '$dir' should be readable by the group"
995 fi
996 if test $((($perm & 0020) == 0020)) = 1; then
997 warning "Certificate database '$dir' must not be writable by the group"
998 rc=1
999 fi
1000 if test $((($perm & 0010) == 0010)) = 0; then
1001 warning "Certificate database '$dir' should be searchable by the group"
1002 fi
1003 if test $((($perm & 0004) == 0004)) = 0; then
1004 warning "Certificate database '$dir' should be readable by others"
1005 fi
1006 if test $((($perm & 0002) == 0002)) = 1; then
1007 warning "Certificate database '$dir' must not be writable by others"
1008 rc=1
1009 fi
1010 if test $((($perm & 0001) == 0001)) = 0; then
1011 warning "Certificate database '$dir' should be searchable by others"
1012 fi
1013
1014 # Now check the permissions of the critical files.
1015 check_db_file $dir/cert8.db $euid $user || rc=1
1016 check_db_file $dir/key3.db $euid $user || rc=1
1017 check_db_file $dir/secmod.db $euid $user || rc=1
1018
1019 test $rc = 1 && warning "Unable to use certificate database '$dir' due to errors"
1020
1021 return $rc
1022 }
1023
1024 # function: check_db_file FILENAME [ EUID USER ]
1025 #
1026 # Check the security of the given database file.
1027 function check_db_file {
1028 local file="$1"
1029 local rc=0
1030
1031 # Check that we have been given a file
1032 if ! test -e $file; then
1033 warning "Certificate database file '$file' does not exist"
1034 return 1
1035 fi
1036 if ! test -f $file; then
1037 warning "Certificate database file '$file' is not a regular file"
1038 return 1
1039 fi
1040
1041 # If euid has been specified, then this directory must be owned by that
1042 # user.
1043 if test "X$euid" != "X"; then
1044 local ownerid=`stat -c "%u" $file`
1045 if test "X$ownerid" != "X$euid"; then
1046 warning "Certificate database file '$file' must be owned by $user"
1047 rc=1
1048 fi
1049 fi
1050
1051 # Check that we can read the file
1052 if ! test -r $file; then
1053 warning "Certificate database file '$file' is not readble"
1054 rc=1
1055 fi
1056
1057 # Check the access permissions of the file
1058 local perm=0`stat -c "%a" $file`
1059 if test $((($perm & 0400) == 0400)) = 0; then
1060 warning "Certificate database file '$file' should be readable by the owner"
1061 fi
1062 if test $((($perm & 0200) == 0200)) = 0; then
1063 warning "Certificate database file '$file' should be writeable by the owner"
1064 fi
1065 if test $((($perm & 0100) == 0100)) = 1; then
1066 warning "Certificate database file '$file' must not be executable by the owner"
1067 rc=1
1068 fi
1069 if test $((($perm & 0040) == 0040)) = 0; then
1070 warning "Certificate database file '$file' should be readable by the group"
1071 fi
1072 if test $((($perm & 0020) == 0020)) = 1; then
1073 warning "Certificate database file '$file' must not be writable by the group"
1074 rc=1
1075 fi
1076 if test $((($perm & 0010) == 0010)) = 1; then
1077 warning "Certificate database file '$file' must not be executable by the group"
1078 rc=1
1079 fi
1080 if test $((($perm & 0004) == 0004)) = 0; then
1081 warning "Certificate database file '$file' should be readable by others"
1082 fi
1083 if test $((($perm & 0002) == 0002)) = 1; then
1084 warning "Certificate database file '$file' must not be writable by others"
1085 rc=1
1086 fi
1087 if test $((($perm & 0001) == 0001)) = 1; then
1088 warning "Certificate database file '$file' must not be executable by others"
1089 rc=1
1090 fi
1091
1092 return $rc
1093 }
1094
1095 # function: warning [ MESSAGE ]
1096 #
1097 # Warning error
1098 # Prints its arguments to stderr
1099 function warning {
1100 echo "$0: WARNING:" "$@" >&2
1101 }
1102
1103 # function: fatal [ MESSAGE ]
1104 #
1105 # Fatal error
1106 # Prints its arguments to stderr and exits
1107 function fatal {
1108 echo "$0: ERROR:" "$@" >&2
1109 cleanup
1110 exit 1
1111 }
1112
1113 # function cleanup
1114 #
1115 # Cleanup work files unless asked to keep them.
1116 function cleanup {
1117 # Clean up.
1118 cd $tmpdir_env
1119 if test $keep_temps != 1; then
1120 rm -fr $tmpdir_client
1121 rm -f $zip_client
1122 rm -f $zip_server
1123 rm -fr $tmpdir_server
1124 fi
1125 }
1126
1127 # function: terminate
1128 #
1129 # Terminate gracefully.
1130 function terminate {
1131 # Clean up
1132 echo "$0: terminated by signal" >&2
1133 cleanup
1134
1135 # Kill any running staprun job
1136 kill -s SIGTERM '%?staprun' 2>/dev/null
1137
1138 # Kill any stap-client-connect job
1139 kill -s SIGTERM '%${stap_pkglibexecdir}stap-client-connect' 2>/dev/null
1140
1141 exit 1
1142 }
1143
1144 # function: interrupt
1145 #
1146 # Pass an interrupt (ctrl-C) to staprun
1147 function interrupt {
1148 # Kill any stap-client-connect job
1149 # SIGINT won't do it.
1150 kill -s SIGTERM '%${stap_pkglibexecdir}stap-client-connect' 2>/dev/null
1151
1152 # If staprun was not running, then exit.
1153 cleanup
1154 exit 1
1155 }
1156
1157 # function: ignore_signal
1158 #
1159 # Called in order to ignore a signal
1160 function ignore_signal {
1161 :
1162 }
1163
1164 #-----------------------------------------------------------------------------
1165 # Beginning of main line execution.
1166 #-----------------------------------------------------------------------------
1167 initialization
1168 parse_options "$@"
1169 create_request
1170 package_request
1171 find_and_connect_to_server
1172 unpack_response
1173 process_response
1174 maybe_call_staprun
1175 cleanup
1176
1177 exit $rc
This page took 0.095209 seconds and 5 git commands to generate.