1#!@BASH_SHELL@ 2 3# 4# Script to manage a Samba file-sharing service component. 5# Unline NFS, this should be placed at the top level of a service 6# because it will try to gather information necessary to run the 7# smbd/nmbd daemons at run-time from the service structure. 8# 9# Copyright (C) 1997-2003 Sistina Software, Inc. All rights reserved. 10# Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. 11# 12# This program is free software; you can redistribute it and/or 13# modify it under the terms of the GNU General Public License 14# as published by the Free Software Foundation; either version 2 15# of the License, or (at your option) any later version. 16# 17# This program is distributed in the hope that it will be useful, 18# but WITHOUT ANY WARRANTY; without even the implied warranty of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20# GNU General Public License for more details. 21# 22# You should have received a copy of the GNU General Public License 23# along with this program; if not, write to the Free Software 24# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 25# 26# Author(s): 27# Lon Hohberger (lhh at redhat.com) 28# Tim Burke (tburke at redhat.com) 29# 30 31LC_ALL=C 32LANG=C 33PATH=/bin:/sbin:/usr/bin:/usr/sbin 34export LC_ALL LANG PATH 35 36# 37# Definitions! 38# 39declare SAMBA_CONFIG_DIR=/etc/samba 40declare SMBD_COMMAND=/usr/sbin/smbd 41declare NMBD_COMMAND=/usr/sbin/nmbd 42declare KILLALL_COMMAND=/usr/bin/killall 43declare SAMBA_PID_DIR=/var/run/samba 44declare SAMBA_LOCK_DIR=/var/cache/samba 45 46# 47# gross globals 48# 49declare -a ipkeys 50declare -a fskeys 51 52# Don't change please :) 53_FAIL=255 54 55. $(dirname $0)/ocf-shellfuncs 56 57meta_data() 58{ 59 cat <<EOT 60<?xml version="1.0"?> 61<resource-agent version="rgmanager 2.0" name="smb"> 62 <version>1.0</version> 63 64 <longdesc lang="en"> 65 Dynamic smbd/nmbd resource agent 66 </longdesc> 67 <shortdesc lang="en"> 68 Dynamic smbd/nmbd resource agent 69 </shortdesc> 70 71 <parameters> 72 <parameter name="name" unique="1" primary="1"> 73 <longdesc lang="en"> 74 Samba Symbolic Name. This name will 75 correspond to /etc/samba/smb.conf.NAME 76 </longdesc> 77 <shortdesc lang="en"> 78 Samba Name 79 </shortdesc> 80 <content type="string"/> 81 </parameter> 82 83 <parameter name="workgroup"> 84 <longdesc lang="en"> 85 Workgroup name 86 </longdesc> 87 <shortdesc lang="en"> 88 Workgroup name 89 </shortdesc> 90 <content type="string" default="LINUXCLUSTER"/> 91 </parameter> 92 93 <parameter name="service_name" inherit="service%name"> 94 <longdesc lang="en"> 95 Inherit the service name. We need to know 96 the service name in order to determine file 97 systems and IPs for this smb service. 98 </longdesc> 99 <shortdesc lang="en"> 100 Inherit the service name. 101 </shortdesc> 102 <content type="string"/> 103 </parameter> 104 </parameters> 105 106 <actions> 107 <action name="start" timeout="0"/> 108 <action name="stop" timeout="0"/> 109 110 <!-- This is just a wrapper for LSB init scripts, so monitor 111 and status can't have a timeout, nor do they do any extra 112 work regardless of the depth --> 113 <action name="status" interval="30s" timeout="0"/> 114 <action name="monitor" interval="30s" timeout="0"/> 115 116 <action name="meta-data" timeout="0"/> 117 <action name="validate-all" timeout="0"/> 118 </actions> 119</resource-agent> 120EOT 121} 122 123 124# 125# Usage: ccs_get key 126# 127ccs_get() 128{ 129 declare outp 130 declare key 131 132 [ -n "$1" ] || return $_FAIL 133 134 key="$*" 135 136 outp=$(ccs_tool query "$key" 2>&1) 137 if [ $? -ne 0 ]; then 138 if [ "$outp" = "${outp/No data available/}" ] || [ "$outp" = "${outp/Operation not permitted/}" ]; then 139 ocf_log err "$outp ($key)" 140 return $_FAIL 141 fi 142 143 # no real error, just no data available 144 return 0 145 fi 146 147 echo $outp 148 149 return 0 150} 151 152 153# 154# Build a list of service IP keys; traverse refs if necessary 155# 156get_service_ip_keys() 157{ 158 declare svc=$1 159 declare -i x y=0 160 declare outp 161 declare key 162 163 # 164 # Find service-local IP keys 165 # 166 x=1 167 while : ; do 168 key="/cluster/rm/service[@name=\"$svc\"]/ip[$x]" 169 170 # 171 # Try direct method 172 # 173 outp=$(ccs_get "$key/@address") 174 if [ $? -ne 0 ]; then 175 return 1 176 fi 177 178 # 179 # Try by reference 180 # 181 if [ -z "$outp" ]; then 182 outp=$(ccs_get "$key/@ref") 183 if [ $? -ne 0 ]; then 184 return 1 185 fi 186 key="/cluster/rm/resources/ip[@address=\"$outp\"]" 187 fi 188 189 if [ -z "$outp" ]; then 190 break 191 fi 192 193 #ocf_log debug "IP $outp found @ $key" 194 195 ipkeys[$y]="$key" 196 197 ((y++)) 198 ((x++)) 199 done 200 201 ocf_log debug "$y IP addresses found for $svc/$OCF_RESKEY_name" 202 203 return 0 204} 205 206 207# 208# Build a list of service fs keys, traverse refs if necessary 209# 210get_service_fs_keys() 211{ 212 declare svc=$1 213 declare -i x y=0 214 declare outp 215 declare key 216 217 # 218 # Find service-local IP keys 219 # 220 x=1 221 while : ; do 222 key="/cluster/rm/service[@name=\"$svc\"]/fs[$x]" 223 224 # 225 # Try direct method 226 # 227 outp=$(ccs_get "$key/@name") 228 if [ $? -ne 0 ]; then 229 return 1 230 fi 231 232 # 233 # Try by reference 234 # 235 if [ -z "$outp" ]; then 236 outp=$(ccs_get "$key/@ref") 237 if [ $? -ne 0 ]; then 238 return 1 239 fi 240 key="/cluster/rm/resources/fs[@name=\"$outp\"]" 241 fi 242 243 if [ -z "$outp" ]; then 244 break 245 fi 246 247 #ocf_log debug "filesystem $outp found @ $key" 248 249 fskeys[$y]="$key" 250 251 ((y++)) 252 ((x++)) 253 done 254 255 ocf_log debug "$y filesystems found for $svc/$OCF_RESKEY_name" 256 257 return 0 258} 259 260 261build_ip_list() 262{ 263 declare ipaddrs ipaddr 264 declare -i x=0 265 266 while [ -n "${ipkeys[$x]}" ]; do 267 ipaddr=$(ccs_get "${ipkeys[$x]}/@address") 268 if [ -z "$ipaddr" ]; then 269 break 270 fi 271 272 ipaddrs="$ipaddrs $ipaddr" 273 274 ((x++)) 275 done 276 277 echo $ipaddrs 278} 279 280 281add_sha1() 282{ 283 declare sha1line="# rgmanager-sha1 $(sha1sum "$1")" 284 echo $sha1line >> "$1" 285} 286 287 288verify_sha1() 289{ 290 declare tmpfile="$(mktemp /tmp/smb-$OCF_RESKEY_name.tmp.XXXXXX)" 291 declare current exp 292 293 exp=$(grep "^# rgmanager-sha1.*$1" "$1" | head -1) 294 if [ -z "$exp" ]; then 295 # No sha1 line. We're done. 296 ocf_log debug "No SHA1 info in $1" 297 return 1 298 fi 299 300 # 301 # Find expected sha1 and expected file name 302 # 303 exp=${exp/*sha1 /} 304 exp=${exp/ */} 305 306 grep -v "^# rgmanager-sha1" "$1" > "$tmpfile" 307 current=$(sha1sum "$tmpfile") 308 current=${current/ */} 309 310 rm -f "$tmpfile" 311 312 if [ "$current" = "$exp" ]; then 313 ocf_log debug "SHA1 sum matches for $1" 314 return 0 315 fi 316 ocf_log debug "SHA1 sum does not match for $1" 317 return 1 318} 319 320 321add_fs_entries() 322{ 323 declare conf="$1" 324 declare sharename 325 declare sharepath key 326 327 declare -i x=0 328 329 while [ -n "${fskeys[$x]}" ]; do 330 key="${fskeys[$x]}/@name" 331 332 sharename=$(ccs_get "$key") 333 if [ -z "$sharename" ]; then 334 break 335 fi 336 337 key="${fskeys[$x]}/@mountpoint" 338 sharepath=$(ccs_get "$key") 339 if [ -z "$sharepath" ]; then 340 break 341 fi 342 343 cat >> "$conf" <<EODEV 344[$sharename] 345 comment = Auto-generated $sharename share 346 # Hide the secret cluster files 347 veto files = /.clumanager/.rgmanager/ 348 browsable = yes 349 writable = no 350 public = yes 351 path = $sharepath 352 353EODEV 354 355 ((x++)) 356 done 357} 358 359 360# 361# Generate the samba configuration if neede for this service. 362# 363gen_smb_conf() 364{ 365 declare conf="$1" 366 declare lvl="debug" 367 368 if [ -f "$conf" ]; then 369 verify_sha1 "$conf" 370 if [ $? -ne 0 ]; then 371 ocf_log debug "Config file changed; skipping" 372 return 0 373 fi 374 else 375 lvl="info" 376 fi 377 378 ocf_log $lvl "Creating $conf" 379 380 get_service_ip_keys "$OCF_RESKEY_service_name" 381 get_service_fs_keys "$OCF_RESKEY_service_name" 382 383 cat > "$conf" <<EOT 384# 385# "$conf" 386# 387# This template configuration wass automatically generated, and will 388# be automatically regenerated if removed. Please modify this file to 389# speficy subdirectories and/or client access permissions. 390# 391# Once this file has been altered, automatic re-generation will stop. 392# Remember to copy this file to all other cluster members after making 393# changes, or your SMB service will not operate correctly. 394# 395# From a cluster perspective, the key fields are: 396# lock directory - must be unique per samba service. 397# bind interfaces only - must be present set to yes. 398# interfaces - must be set to service floating IP address. 399# path - must be the service mountpoint or subdirectory thereof. 400# 401 402[global] 403 workgroup = $OCF_RESKEY_workgroup 404 pid directory = /var/run/samba/$OCF_RESKEY_name 405 lock directory = /var/cache/samba/$OCF_RESKEY_name 406 log file = /var/log/samba/%m.log 407 #private dir = /var/ 408 encrypt passwords = yes 409 bind interfaces only = yes 410 netbios name = ${OCF_RESKEY_name/ /_} 411 412 # 413 # Interfaces are based on ip resources at the top level of 414 # "$OCF_RESKEY_service_name"; IPv6 addresses may or may not 415 # work correctly. 416 # 417 interfaces = $(build_ip_list) 418 419# 420# Shares based on fs resources at the top level of "$OCF_RESKEY_service_name" 421# 422EOT 423 add_fs_entries "$conf" 424 add_sha1 "$conf" 425 426 return 0 427} 428 429 430# 431# Kill off the specified PID 432# (from clumanager 1.0.x/1.2.x) 433# 434# Killing off the samba daemons was miserable to implement, merely 435# because killall doesn't distinguish by program commandline. 436# Consequently I had to implement these routines to selectively pick 'em off. 437# 438# Kills of either the {smbd|nmbd} which is running and was started with 439# the specified argument. Can't use `killall` to do this because it 440# doesn't allow you to distinguish which process to kill based on any 441# of the program arguments. 442# 443# This routine is also called on "status" checks. In this case it doesn't 444# actually kill anything. 445# 446# Parameters: 447# daemonName - daemon name, can be either smbd or nmbd 448# command - [stop|start|status] 449# arg - argument passed to daemon. In this case its not the 450# full set of program args, rather its really just the 451# samba config file. 452# 453# Returns: 0 - success (or the daemon isn't currently running) 454# 1 - failure 455# 456kill_daemon_by_arg() 457{ 458 declare daemonName=$1 459 declare action=$2 460 declare arg=$3 461 # Create a unique temporary file to stash off intermediate results 462 declare tmpfile_str=/tmp/sambapids.XXXXXX 463 declare tmpfile 464 declare ret 465 466 tmpfile=$(mktemp $tmpfile_str); ret_val=$? 467 468 if [ -z "$tmpfile" ]; then 469 ocf_log err "kill_daemon_by_arg: Can't create tmp file" 470 return $_FAIL 471 fi 472 473 # Mumble, need to strip off the /etc/samba portion, otherwise the 474 # grep pattern matching will fail. 475 declare confFile="$(basename $arg)" 476 477 # First generate a list of candidate pids. 478 pidof $daemonName > $tmpfile 479 if [ $? -ne 0 ]; then 480 ocf_log debug "kill_daemon_by_arg: no pids for $daemonName" 481 rm -f $tmpfile 482 case "$action" in 483 'stop') 484 return 0 485 ;; 486 'status') 487 return $_FAIL 488 ;; 489 esac 490 return 0 491 fi 492 493 # If you don't find any matching daemons for a "stop" operation, thats 494 # considered success; whereas for "status" inquiries its a failure. 495 case "$action" in 496 'stop') 497 ret=0 498 ;; 499 'status') 500 ret=$_FAIL 501 ;; 502 esac 503 # 504 # At this point tmpfile contains a set of pids for the corresponding 505 # {smbd|nmbd}. Now look though this candidate set of pids and compare 506 # the program arguments (samba config file name). This distinguishes 507 # which ones should be killed off. 508 # 509 declare daemonPid="" 510 for daemonPid in $(cat $tmpfile); do 511 declare commandLine=$(cat /proc/$daemonPid/cmdline) 512 declare confBase="$(basename $commandLine)" 513 if [ "$confBase" = "$confFile" ]; then 514 case "$action" in 515 'status') 516 rm -f $tmpfile 517 return 0 518 ;; 519 esac 520 kill_daemon_pid $daemonPid 521 if [ $? -ne 0 ]; then 522 ret=$_FAIL 523 ocf_log err \ 524 "kill_daemon_by_arg: kill_daemon_pid $daemonPid failed" 525 else 526 ocf_log debug \ 527 "kill_daemon_by_arg: kill_daemon_pid $daemonPid success" 528 fi 529 fi 530 done 531 rm -f $tmpfile 532 return $ret 533} 534 535 536# 537# Kill off the specified PID 538# (from clumanager 1.0.x/1.2.x) 539# 540kill_daemon_pid() 541{ 542 declare pid=$1 543 declare retval=0 544 545 546 kill -TERM $pid 547 if [ $? -eq 0 ]; then 548 ocf_log debug "Samba: successfully killed $pid" 549 else 550 ocf_log debug "Samba: failed to kill $pid" 551 retval=$_FAIL 552 fi 553 return $retval 554} 555 556 557share_start_stop() 558{ 559 declare command=$1 560 declare conf="$SAMBA_CONFIG_DIR/smb.conf.$OCF_RESKEY_name" 561 declare smbd_command 562 declare nmbd_command 563 declare netbios_name 564 565 # 566 # Specify daemon options 567 # -D = spawn off as separate daemon 568 # -s = the following arg specifies the config file 569 # 570 declare smbd_options="-D -s" 571 declare nmbd_options="-D -s" 572 573 if [ "$command" = "start" ]; then 574 gen_smb_conf "$conf" 575 else 576 if ! [ -f "$conf" ]; then 577 ocf_log warn "\"$conf\" missing during $command" 578 fi 579 fi 580 581 # 582 # On clusters with multiple samba shares, we need to ensure (as much 583 # as possible) that each service is advertised as a separate netbios 584 # name. 585 # 586 # Generally, the admin sets this in smb.conf.NAME - but since 587 # it is not required, we need another option. Consequently, we use 588 # smb instance name (which must be unique) 589 # 590 if [ -f "$conf" ]; then 591 grep -qe "^\([[:space:]]\+n\|n\)etbios[[:space:]]\+name[[:space:]]*=[[:space:]]*[[:alnum:]]\+" "$conf" 592 if [ $? -ne 0 ]; then 593 594 netbios_name=$OCF_RESKEY_name 595 596 ocf_log notice "Using $netbios_name as NetBIOS name (service $OCF_RESKEY_service_name)" 597 nmbd_options=" -n $netbios_name $nmbd_options" 598 fi 599 fi 600 601 case $command in 602 start) 603 ocf_log info "Starting Samba instance \"$OCF_RESKEY_name\"" 604 mkdir -p "$SAMBA_PID_DIR/$OCF_RESKEY_name" 605 mkdir -p "$SAMBA_LOCK_DIR/$OCF_RESKEY_name" 606 607 [ -f "$SMBD_COMMAND" ] || exit $OCF_ERR_INSTALLED 608 [ -f "$NMBD_COMMAND" ] || exit $OCF_ERR_INSTALLED 609 610 # Kick off the per-service smbd 611 $SMBD_COMMAND $smbd_options "$conf" 612 ret_val=$? 613 if [ $ret_val -ne 0 ]; then 614 ocf_log err "Samba service failed: $SMBD_COMMAND $smbd_options \"$conf\"" 615 return $_FAIL 616 fi 617 ocf_log debug "Samba service succeeded: $SMBD_COMMAND $smbd_options \"$conf\"" 618 619 # Kick off the per-service nmbd 620 $NMBD_COMMAND $nmbd_options "$conf" 621 ret_val=$? 622 if [ $ret_val -ne 0 ]; then 623 ocf_log err "Samba service failed: $NMBD_COMMAND $nmbd_options \"$conf\"" 624 return $_FAIL 625 fi 626 ocf_log debug "Samba service succeeded: $NMBD_COMMAND $nmbd_options \"$conf\"" 627 ;; 628 stop) 629 ocf_log info "Stopping Samba instance \"$OCF_RESKEY_name\"" 630 631 kill_daemon_by_arg "nmbd" $command "$conf" 632 kill_daemon_by_arg "smbd" $command "$conf" 633 if [ "$SAMBA_PID_DIR/$OCF_RESKEY_name" != "/" ]; then 634 pushd "$SAMBA_PID_DIR" &> /dev/null 635 rm -rf "$OCF_RESKEY_name" 636 popd &> /dev/null 637 fi 638 if [ "$SAMBA_LOCK_DIR/$OCF_RESKEY_name" != "/" ]; then 639 pushd "$SAMBA_LOCK_DIR" &> /dev/null 640 rm -rf "$OCF_RESKEY_name" 641 popd &> /dev/null 642 fi 643 ;; 644 status) 645 ocf_log debug "Checking Samba instance \"$OCF_RESKEY_name\"" 646 kill_daemon_by_arg "nmbd" $command "$conf" 647 if [ $? -ne 0 ]; then 648 ocf_log err \ 649 "share_start_stop: nmbd for service $svc_name died!" 650 return $_FAIL 651 fi 652 kill_daemon_by_arg "smbd" $command "$conf" 653 if [ $? -ne 0 ]; then 654 ocf_log err \ 655 "share_start_stop: nmbd for service $svc_name died!" 656 return $_FAIL 657 fi 658 ;; 659 esac 660} 661 662 663verify_all() 664{ 665 [ -z "$OCF_RESKEY_workgroup" ] && export OCF_RESKEY_workgroup="LINUXCLUSTER" 666 [ -n "${OCF_RESKEY_name}" ] || exit $OCF_ERR_ARGS # Invalid Argument 667 if [ -z "${OCF_RESKEY_service_name}" ]; then 668 ocf_log ERR "Samba service ${OCF_RESKEY_name} is not the child of a service" 669 exit $OCF_ERR_ARGS 670 fi 671} 672 673case $1 in 674 meta-data) 675 meta_data 676 exit 0 677 ;; 678 start|stop) 679 verify_all 680 share_start_stop $1 681 exit $? 682 ;; 683 status|monitor) 684 verify_all 685 share_start_stop status 686 exit $? 687 ;; 688 validate-all) 689 verify_all 690 echo "Yer radio's workin', driver!" 691 exit 0 692 ;; 693 *) 694 echo "usage: $0 {start|stop|status|monitor|meta-data|validate-all}" 695 exit $OCF_ERR_UNIMPLEMENTED 696 ;; 697esac 698 699