github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/pfs-crash/main.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package main 5 6 import ( 7 "bufio" 8 "bytes" 9 "container/list" 10 cryptoRand "crypto/rand" 11 "encoding/json" 12 "fmt" 13 "io/ioutil" 14 "log" 15 "math/big" 16 mathRand "math/rand" 17 "net" 18 "net/http" 19 "os" 20 "os/exec" 21 "os/signal" 22 "path/filepath" 23 "strconv" 24 "strings" 25 "time" 26 27 "golang.org/x/sys/unix" 28 29 "github.com/swiftstack/ProxyFS/conf" 30 "github.com/swiftstack/ProxyFS/httpserver" 31 "github.com/swiftstack/ProxyFS/transitions" 32 ) 33 34 const ( 35 proxyfsdHalterMinHaltAfterCount = uint64(400) 36 proxyfsdHalterMaxHaltAfterCount = uint64(800) 37 38 proxyfsdMinKillDelay = 10 * time.Second 39 proxyfsdMaxKillDelay = 20 * time.Second 40 41 proxyfsdPollDelay = 100 * time.Millisecond 42 43 openDirPollDelay = 100 * time.Millisecond 44 45 pseudoRandom = false 46 pseudoRandomSeed = int64(0) 47 ) 48 49 type queryMethodType uint16 50 51 const ( 52 queryMethodGET queryMethodType = iota 53 queryMethodPOST 54 ) 55 56 var ( 57 confFile string 58 fuseMountPointName string 59 haltLabelStrings []string 60 includeHalterTriggers bool 61 ipAddrTCPPort string 62 mathRandSource *mathRand.Rand // A source for pseudo-random numbers (if selected) 63 proxyfsdArgs []string 64 proxyfsdCmd *exec.Cmd 65 proxyfsdCmdWaitChan chan error 66 timeoutChan chan bool 67 trafficCmd *exec.Cmd 68 trafficCmdWaitChan chan error 69 trafficScript string 70 volumeName string 71 72 signalExpandedStringMap = map[string]string{"interrupt": "SIGINT(2)", "terminated": "SIGTERM(15)", "killed": "SIGKILL(9)"} 73 ) 74 75 func usage() { 76 fmt.Printf("%v {+|-}[<trafficScript>] <volumeName> <confFile> [<confOverride>]*\n", os.Args[0]) 77 fmt.Println(" where:") 78 fmt.Println(" + indicates to include halter trigger to halt ProxyFS") 79 fmt.Println(" - indicates to not incluce halter trigger to halt ProxyFS") 80 fmt.Println(" <trafficScript> launch trafficScript bash script to generate workload") 81 fmt.Println() 82 fmt.Println("Note: If trafficScript is supplied, the script should infinitely loop.") 83 fmt.Println(" The $1 arg to trafficScript will specify the FUSE MountPoint.") 84 } 85 86 func main() { 87 var ( 88 confMap conf.ConfMap 89 confStrings []string 90 contentsAsStrings []string 91 err error 92 haltLabelString string 93 haltLabelStringSplit []string 94 httpServerTCPPort uint16 95 httpStatusCode int 96 lenArgs int 97 mkproxyfsArgs []string 98 mkproxyfsCmd *exec.Cmd 99 nextHalterTriggerIndex int 100 peerSectionName string 101 privateIPAddr string 102 randomHaltAfterCount uint64 103 randomKillDelay time.Duration 104 signalChan chan os.Signal 105 signalToSend os.Signal 106 triggerBoolAndTrafficScript string 107 whoAmI string 108 ) 109 110 lenArgs = len(os.Args) 111 if 1 == lenArgs { 112 usage() 113 os.Exit(0) 114 } 115 if 4 > lenArgs { 116 usage() 117 os.Exit(-1) 118 } 119 120 triggerBoolAndTrafficScript = os.Args[1] 121 122 if 0 == len(triggerBoolAndTrafficScript) { 123 usage() 124 os.Exit(-1) 125 } 126 127 switch triggerBoolAndTrafficScript[0] { 128 case '+': 129 includeHalterTriggers = true 130 case '-': 131 includeHalterTriggers = false 132 default: 133 usage() 134 os.Exit(-1) 135 } 136 137 trafficScript = triggerBoolAndTrafficScript[1:] 138 139 volumeName = os.Args[2] 140 confFile = os.Args[3] 141 142 confMap, err = conf.MakeConfMapFromFile(confFile) 143 if nil != err { 144 log.Fatal(err) 145 } 146 147 mkproxyfsArgs = []string{"-F", volumeName, confFile} 148 proxyfsdArgs = []string{confFile} 149 150 if 4 < lenArgs { 151 confStrings = os.Args[4:] 152 153 err = confMap.UpdateFromStrings(confStrings) 154 if nil != err { 155 log.Fatalf("failed to apply config overrides: %v", err) 156 } 157 158 mkproxyfsArgs = append(mkproxyfsArgs, confStrings...) 159 proxyfsdArgs = append(proxyfsdArgs, confStrings...) 160 } 161 162 // Upgrade confMap if necessary 163 err = transitions.UpgradeConfMapIfNeeded(confMap) 164 if nil != err { 165 log.Fatalf("Failed to upgrade config: %v", err) 166 } 167 168 whoAmI, err = confMap.FetchOptionValueString("Cluster", "WhoAmI") 169 if nil != err { 170 log.Fatal(err) 171 } 172 173 peerSectionName = "Peer:" + whoAmI 174 175 privateIPAddr, err = confMap.FetchOptionValueString(peerSectionName, "PrivateIPAddr") 176 if nil != err { 177 log.Fatal(err) 178 } 179 180 httpServerTCPPort, err = confMap.FetchOptionValueUint16("HTTPServer", "TCPPort") 181 if nil != err { 182 log.Fatal(err) 183 } 184 185 ipAddrTCPPort = net.JoinHostPort(privateIPAddr, strconv.Itoa(int(httpServerTCPPort))) 186 187 fuseMountPointName, err = confMap.FetchOptionValueString("Volume:"+volumeName, "FUSEMountPointName") 188 if nil != err { 189 log.Fatal(err) 190 } 191 192 mkproxyfsCmd = exec.Command("mkproxyfs", mkproxyfsArgs...) 193 194 err = mkproxyfsCmd.Run() 195 if nil != err { 196 log.Fatalf("mkproxyfsCmd.Run() failed: %v", err) 197 } 198 199 log.Printf("Call to mkproxyfsCmd.Run() succeeded") 200 201 proxyfsdCmdWaitChan = make(chan error, 1) 202 203 cleanDirectoryUnderFUSEMountPointName() 204 205 launchProxyFSRunSCRUBAndFSCK() 206 207 log.Printf("Initial call to launchProxyFSRunSCRUBAndFSCK() succeeded") 208 209 if includeHalterTriggers { 210 httpStatusCode, _, contentsAsStrings, err = queryProxyFS(queryMethodGET, "/trigger", "") 211 if nil != err { 212 log.Printf("queryProxyFS() failed: %v", err) 213 stopProxyFS(unix.SIGTERM) 214 os.Exit(-1) 215 } 216 if http.StatusOK != httpStatusCode { 217 log.Printf("queryProxyFS() returned unexpected httpStatusCode: %v", httpStatusCode) 218 stopProxyFS(unix.SIGTERM) 219 os.Exit(-1) 220 } 221 222 haltLabelStrings = make([]string, 0) 223 224 for _, contentString := range contentsAsStrings { 225 haltLabelStringSplit = strings.Split(contentString, " ") 226 if 0 == len(haltLabelStringSplit) { 227 log.Printf("queryProxyFS() returned unexpected contentString: %v", contentString) 228 stopProxyFS(unix.SIGTERM) 229 os.Exit(-1) 230 } 231 haltLabelString = haltLabelStringSplit[0] 232 if "" == haltLabelString { 233 log.Printf("queryProxyFS() returned unexpected empty contentString") 234 stopProxyFS(unix.SIGTERM) 235 os.Exit(-1) 236 } 237 238 if !strings.HasPrefix(haltLabelString, "halter.") { 239 haltLabelStrings = append(haltLabelStrings, haltLabelString) 240 } 241 } 242 243 if 0 == len(haltLabelStrings) { 244 log.Printf("No halter.Arm() calls found - disabling") 245 includeHalterTriggers = false 246 } else { 247 log.Printf("Will arm haltLabelStrings:") 248 for _, haltLabelString = range haltLabelStrings { 249 log.Printf(" %v", haltLabelString) 250 } 251 } 252 } else { 253 log.Printf("No halter.Arm() calls scheduled") 254 } 255 256 signalChan = make(chan os.Signal, 1) 257 258 signal.Notify(signalChan, unix.SIGINT, unix.SIGTERM) 259 260 timeoutChan = make(chan bool, 1) 261 262 trafficCmdWaitChan = make(chan error, 1) 263 264 // Loop through causing ProxyFS to halt via: 265 // SIGINT 266 // SIGTERM 267 // SIGKILL 268 // halter.Trigger() on each of haltLabelStrings 269 // until SIGINT or SIGTERM 270 271 signalToSend = unix.SIGINT 272 273 for { 274 if nil == signalToSend { 275 randomHaltAfterCount = proxyfsdRandomHaltAfterCount() 276 log.Printf("Arming trigger %v with haltAfterCount == %v", haltLabelStrings[nextHalterTriggerIndex], randomHaltAfterCount) 277 httpStatusCode, _, _, err = queryProxyFS(queryMethodPOST, "/trigger/"+haltLabelStrings[nextHalterTriggerIndex]+"?count="+strconv.FormatUint(randomHaltAfterCount, 10), "") 278 if nil != err { 279 log.Printf("queryProxyFS() failed: %v", err) 280 stopProxyFS(unix.SIGTERM) 281 os.Exit(-1) 282 } 283 if http.StatusNoContent != httpStatusCode { 284 log.Printf("queryProxyFS() returned unexpected httpStatusCode: %v", httpStatusCode) 285 stopProxyFS(unix.SIGTERM) 286 os.Exit(-1) 287 } 288 } else { 289 randomKillDelay = proxyfsdRandomKillDelay() 290 log.Printf("Will fire %v after %v", signalExpandedStringMap[signalToSend.String()], randomKillDelay) 291 go timeoutWaiter(randomKillDelay) 292 } 293 294 launchTrafficScript() 295 296 select { 297 case _ = <-signalChan: 298 log.Printf("Received SIGINT or SIGTERM... cleanly shutting down ProxyFS") 299 stopTrafficScript() 300 stopProxyFS(unix.SIGTERM) 301 os.Exit(0) 302 case _ = <-timeoutChan: 303 log.Printf("Sending %v to ProxyFS", signalExpandedStringMap[signalToSend.String()]) 304 stopProxyFS(signalToSend) 305 stopTrafficScript() 306 case err = <-proxyfsdCmdWaitChan: 307 log.Printf("ProxyFS has halted due to trigger or other failure") 308 stopTrafficScript() 309 case err = <-trafficCmdWaitChan: 310 log.Printf("trafficScript unexpectedly finished/failed: %v", err) 311 stopProxyFS(unix.SIGTERM) 312 os.Exit(-1) 313 } 314 315 cleanDirectoryUnderFUSEMountPointName() 316 317 launchProxyFSRunSCRUBAndFSCK() 318 319 switch signalToSend { 320 case unix.SIGINT: 321 signalToSend = unix.SIGTERM 322 case unix.SIGTERM: 323 signalToSend = unix.SIGKILL 324 case unix.SIGKILL: 325 if includeHalterTriggers { 326 signalToSend = nil 327 nextHalterTriggerIndex = 0 328 } else { 329 signalToSend = unix.SIGINT 330 } 331 case nil: 332 nextHalterTriggerIndex++ 333 if len(haltLabelStrings) == nextHalterTriggerIndex { 334 signalToSend = unix.SIGINT 335 } 336 default: 337 log.Printf("Logic error... unexpected signalToSend: %v", signalToSend) 338 stopTrafficScript() 339 stopProxyFS(unix.SIGTERM) 340 os.Exit(-1) 341 } 342 } 343 } 344 345 func timeoutWaiter(randomKillDelay time.Duration) { 346 time.Sleep(randomKillDelay) 347 timeoutChan <- true 348 } 349 350 func trafficCmdWaiter() { 351 trafficCmdWaitChan <- trafficCmd.Wait() 352 } 353 354 func stopTrafficScript() { 355 var ( 356 err error 357 pid int 358 pidSliceLast []int 359 pidSliceNow []int 360 umountCmd *exec.Cmd 361 ) 362 363 if "" != trafficScript { 364 pidSliceNow = pstree(trafficCmd.Process.Pid) 365 366 // Send SIGSTOP to all pids in pidSliceNow to prevent more trafficScript processes from being created 367 368 for { 369 for _, pid = range pidSliceNow { 370 err = unix.Kill(pid, unix.SIGSTOP) 371 if nil != err { 372 log.Printf("INFO: unix.Kill(%v, unix.SIGSTOP) failed: %v", pid, err) 373 } 374 } 375 pidSliceLast = pidSliceNow 376 pidSliceNow = pstree(trafficCmd.Process.Pid) 377 if pidSliceEqual(pidSliceLast, pidSliceNow) { 378 break 379 } 380 } 381 382 // Send SIGKILL to all pids in pidSliceNow to actually kill all trafficScript processes 383 384 for { 385 for _, pid = range pidSliceNow { 386 err = unix.Kill(pid, unix.SIGKILL) 387 if nil != err { 388 log.Printf("INFO: unix.Kill(%v, unix.SIGKILL) failed: %v", pid, err) 389 } 390 } 391 pidSliceLast = pidSliceNow 392 pidSliceNow = pstree(trafficCmd.Process.Pid) 393 if pidSliceEqual(pidSliceLast, pidSliceNow) { 394 break 395 } 396 } 397 398 // Force an unmount of fewMountPointName incase any processes of trafficScript are hung on if 399 400 umountCmd = exec.Command("fusermount", "-u", fuseMountPointName) 401 402 err = umountCmd.Run() 403 if nil != err { 404 log.Printf("INFO: umountCmd.Run() failed: %v", err) 405 } 406 407 // Finally, await indicated exit of trafficScript 408 409 _ = <-trafficCmdWaitChan 410 } 411 } 412 413 func launchTrafficScript() { 414 var ( 415 err error 416 ) 417 418 if "" != trafficScript { 419 log.Printf("Launching trafficScript: bash %v %v", trafficScript, fuseMountPointName) 420 421 trafficCmd = exec.Command("bash", trafficScript, fuseMountPointName) 422 423 err = trafficCmd.Start() 424 if nil != err { 425 log.Fatalf("trafficCmd.Start() failed: %v", err) 426 } 427 428 go trafficCmdWaiter() 429 } 430 } 431 432 func proxyfsdCmdWaiter() { 433 proxyfsdCmdWaitChan <- proxyfsdCmd.Wait() 434 } 435 436 func stopProxyFS(signalToSend os.Signal) { 437 var ( 438 err error 439 ) 440 441 err = proxyfsdCmd.Process.Signal(signalToSend) 442 if nil != err { 443 log.Fatalf("proxyfsdCmd.Process.Signal(signalToSend) failed: %v", err) 444 } 445 _ = <-proxyfsdCmdWaitChan 446 } 447 448 func launchProxyFSRunSCRUBAndFSCK() { 449 var ( 450 contentsAsStrings []string 451 err error 452 fsckJob httpserver.JobStatusJSONPackedStruct 453 fsckJobError string 454 httpStatusCode int 455 locationURL string 456 polling bool 457 scrubJob httpserver.JobStatusJSONPackedStruct 458 scrubJobError string 459 ) 460 461 log.Printf("Launching ProxyFS and performing SCRUB & FSCK of %v", volumeName) 462 463 proxyfsdCmd = exec.Command("proxyfsd", proxyfsdArgs...) 464 465 err = proxyfsdCmd.Start() 466 if nil != err { 467 log.Fatalf("proxyfsdCmd.Start() failed: %v", err) 468 } 469 470 go proxyfsdCmdWaiter() 471 472 polling = true 473 for polling { 474 time.Sleep(proxyfsdPollDelay) 475 476 httpStatusCode, locationURL, _, err = queryProxyFS(queryMethodPOST, "/volume/"+volumeName+"/scrub-job", "") 477 if nil == err { 478 polling = false 479 } 480 } 481 482 if http.StatusCreated != httpStatusCode { 483 log.Printf("queryProxyFS(queryMethodPOST,\"/volume/%v/scrub-job\",) returned unexpected httpStatusCode: %v", volumeName, httpStatusCode) 484 stopProxyFS(unix.SIGTERM) 485 os.Exit(-1) 486 } 487 488 polling = true 489 for polling { 490 time.Sleep(proxyfsdPollDelay) 491 492 httpStatusCode, _, contentsAsStrings, err = queryProxyFS(queryMethodGET, locationURL+"?compact=true", "application/json") 493 if nil != err { 494 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) failed: %v", locationURL, err) 495 stopProxyFS(unix.SIGTERM) 496 os.Exit(-1) 497 } 498 if http.StatusOK != httpStatusCode { 499 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned unexpected httpStatusCode: %v", locationURL, httpStatusCode) 500 stopProxyFS(unix.SIGTERM) 501 os.Exit(-1) 502 } 503 if 1 != len(contentsAsStrings) { 504 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned unexpected len(contentsAsStrings): %v", locationURL, len(contentsAsStrings)) 505 } 506 507 err = json.Unmarshal([]byte(contentsAsStrings[0]), &scrubJob) 508 if nil != err { 509 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned undecodable content: %v (err == %v)", locationURL, contentsAsStrings[0], err) 510 stopProxyFS(unix.SIGTERM) 511 os.Exit(-1) 512 } 513 if "" == scrubJob.StartTime { 514 log.Printf("scrubJob unexpectantly missing StartTime value") 515 stopProxyFS(unix.SIGTERM) 516 os.Exit(-1) 517 } 518 if "" != scrubJob.HaltTime { 519 log.Printf("scrubJob contained unexpected HaltTime value: %v", scrubJob.HaltTime) 520 stopProxyFS(unix.SIGTERM) 521 os.Exit(-1) 522 } 523 524 if "" != scrubJob.DoneTime { 525 polling = false 526 } 527 } 528 529 if 0 < len(scrubJob.ErrorList) { 530 if 1 == len(scrubJob.ErrorList) { 531 log.Printf("scrubJob contained unexpected error: %v", scrubJob.ErrorList[0]) 532 } else { 533 log.Printf("scrubJob contained unexpected errors:") 534 for _, scrubJobError = range scrubJob.ErrorList { 535 log.Printf(" %v", scrubJobError) 536 } 537 } 538 stopProxyFS(unix.SIGTERM) 539 os.Exit(-1) 540 } 541 542 httpStatusCode, locationURL, _, err = queryProxyFS(queryMethodPOST, "/volume/"+volumeName+"/fsck-job", "") 543 if nil != err { 544 log.Printf("queryProxyFS(queryMethodPOST,\"/volume/%v/fsck-job\",) failed: %v", volumeName, err) 545 stopProxyFS(unix.SIGTERM) 546 os.Exit(-1) 547 } 548 if http.StatusCreated != httpStatusCode { 549 log.Printf("queryProxyFS(queryMethodPOST,\"/volume/%v/fsck-job\",) returned unexpected httpStatusCode: %v", volumeName, httpStatusCode) 550 stopProxyFS(unix.SIGTERM) 551 os.Exit(-1) 552 } 553 554 polling = true 555 for polling { 556 time.Sleep(proxyfsdPollDelay) 557 558 httpStatusCode, _, contentsAsStrings, err = queryProxyFS(queryMethodGET, locationURL+"?compact=true", "application/json") 559 if nil != err { 560 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) failed: %v", locationURL, err) 561 stopProxyFS(unix.SIGTERM) 562 os.Exit(-1) 563 } 564 if http.StatusOK != httpStatusCode { 565 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned unexpected httpStatusCode: %v", locationURL, httpStatusCode) 566 stopProxyFS(unix.SIGTERM) 567 os.Exit(-1) 568 } 569 if 1 != len(contentsAsStrings) { 570 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned unexpected len(contentsAsStrings): %v", locationURL, len(contentsAsStrings)) 571 } 572 573 err = json.Unmarshal([]byte(contentsAsStrings[0]), &fsckJob) 574 if nil != err { 575 log.Printf("queryProxyFS(queryMethodGET,\"%v?compact=true\",) returned undecodable content: %v (err == %v)", locationURL, contentsAsStrings[0], err) 576 stopProxyFS(unix.SIGTERM) 577 os.Exit(-1) 578 } 579 if "" == fsckJob.StartTime { 580 log.Printf("fsckJob unexpectantly missing StartTime value") 581 stopProxyFS(unix.SIGTERM) 582 os.Exit(-1) 583 } 584 if "" != fsckJob.HaltTime { 585 log.Printf("fsckJob contained unexpected HaltTime value: %v", fsckJob.HaltTime) 586 stopProxyFS(unix.SIGTERM) 587 os.Exit(-1) 588 } 589 590 if "" != fsckJob.DoneTime { 591 polling = false 592 } 593 } 594 595 if 0 < len(fsckJob.ErrorList) { 596 if 1 == len(fsckJob.ErrorList) { 597 log.Printf("fsckJob contained unexpected error: %v", fsckJob.ErrorList[0]) 598 } else { 599 log.Printf("fsckJob contained unexpected errors:") 600 for _, fsckJobError = range fsckJob.ErrorList { 601 log.Printf(" %v", fsckJobError) 602 } 603 } 604 stopProxyFS(unix.SIGTERM) 605 os.Exit(-1) 606 } 607 608 log.Printf("ProxyFS launched; SCRUB & FSCK of %v reported no errors", volumeName) 609 } 610 611 func queryProxyFS(queryMethod queryMethodType, queryURL string, acceptHeader string) (httpStatusCode int, locationURL string, contentsAsStrings []string, err error) { 612 var ( 613 client *http.Client 614 contentsAsByteSlice []byte 615 queryURLWithHost string 616 request *http.Request 617 response *http.Response 618 ) 619 620 queryURLWithHost = "http://" + ipAddrTCPPort + queryURL 621 622 switch queryMethod { 623 case queryMethodGET: 624 request, err = http.NewRequest("GET", queryURLWithHost, nil) 625 case queryMethodPOST: 626 request, err = http.NewRequest("POST", queryURLWithHost, nil) 627 default: 628 log.Fatalf("queryProxyFS(queryMethod==%v,,) invalid", queryMethod) 629 } 630 631 if "" != acceptHeader { 632 request.Header.Add("Accept", acceptHeader) 633 } 634 635 client = &http.Client{} 636 637 response, err = client.Do(request) 638 if nil != err { 639 return 640 } 641 642 defer response.Body.Close() 643 644 httpStatusCode = response.StatusCode 645 646 locationURL = response.Header.Get("Location") 647 648 contentsAsByteSlice, err = ioutil.ReadAll(response.Body) 649 if nil != err { 650 return 651 } 652 653 contentsAsStrings = strings.Split(string(contentsAsByteSlice), "\n") 654 if "" == contentsAsStrings[len(contentsAsStrings)-1] { 655 contentsAsStrings = contentsAsStrings[:len(contentsAsStrings)-1] 656 } 657 658 return 659 } 660 661 func proxyfsdRandomHaltAfterCount() (haltAfterCount uint64) { 662 var ( 663 bigN *big.Int 664 bigR *big.Int 665 err error 666 ) 667 668 if pseudoRandom { 669 if nil == mathRandSource { 670 mathRandSource = mathRand.New(mathRand.NewSource(pseudoRandomSeed)) 671 } 672 haltAfterCount = uint64(mathRandSource.Int63n(int64(proxyfsdHalterMaxHaltAfterCount-proxyfsdHalterMinHaltAfterCount)+1)) + proxyfsdHalterMinHaltAfterCount 673 } else { 674 bigN = big.NewInt(int64(proxyfsdHalterMaxHaltAfterCount-proxyfsdHalterMinHaltAfterCount) + 1) 675 bigR, err = cryptoRand.Int(cryptoRand.Reader, bigN) 676 if nil != err { 677 log.Fatalf("cryptoRand.Int(cryptoRand.Reader, bigN) failed: %v", err) 678 } 679 haltAfterCount = bigR.Uint64() + proxyfsdHalterMinHaltAfterCount 680 } 681 682 return 683 } 684 685 func proxyfsdRandomKillDelay() (killDelay time.Duration) { 686 var ( 687 bigN *big.Int 688 bigR *big.Int 689 err error 690 ) 691 692 if pseudoRandom { 693 if nil == mathRandSource { 694 mathRandSource = mathRand.New(mathRand.NewSource(pseudoRandomSeed)) 695 } 696 killDelay = time.Duration(mathRandSource.Int63n(int64(proxyfsdMaxKillDelay-proxyfsdMinKillDelay)+1)) + proxyfsdMinKillDelay 697 } else { 698 bigN = big.NewInt(int64(proxyfsdMaxKillDelay-proxyfsdMinKillDelay) + 1) 699 bigR, err = cryptoRand.Int(cryptoRand.Reader, bigN) 700 if nil != err { 701 log.Fatalf("cryptoRand.Int(cryptoRand.Reader, bigN) failed: %v", err) 702 } 703 killDelay = time.Duration(bigR.Uint64()) + proxyfsdMinKillDelay 704 } 705 706 return 707 } 708 709 func pstree(topPid int) (pidSlice []int) { 710 var ( 711 err error 712 ok bool 713 pid int 714 pidChildren []int 715 pidChildrenMap map[int][]int 716 pidList *list.List 717 pidOnList *list.Element 718 ppid int 719 psOutputByteSlice []byte 720 psOutputBufioReader *bufio.Reader 721 psOutputBytesReader *bytes.Reader 722 psOutputLine string 723 sscanfN int 724 ) 725 726 psOutputByteSlice, err = exec.Command("ps", "-e", "-o", "pid,ppid").Output() 727 if nil != err { 728 log.Fatalf("exec.Command(\"ps\", \"-e\", \"-o\", \"pid,ppid\").Output failed: %v", err) 729 } 730 731 psOutputBytesReader = bytes.NewReader(psOutputByteSlice) 732 psOutputBufioReader = bufio.NewReader(psOutputBytesReader) 733 734 pidChildrenMap = make(map[int][]int) 735 736 for { 737 psOutputLine, err = psOutputBufioReader.ReadString(byte(0x0A)) 738 if nil != err { 739 break 740 } 741 sscanfN, err = fmt.Sscanf(psOutputLine, "%d %d\n", &pid, &ppid) 742 if nil != err { 743 continue 744 } 745 if 2 != sscanfN { 746 continue 747 } 748 749 pidChildren, ok = pidChildrenMap[ppid] 750 if !ok { 751 pidChildren = make([]int, 0, 1) 752 } 753 pidChildren = append(pidChildren, pid) 754 pidChildrenMap[ppid] = pidChildren 755 } 756 757 pidList = list.New() 758 759 pstreeStep(topPid, pidChildrenMap, pidList) 760 761 pidSlice = make([]int, 0, pidList.Len()) 762 763 for pidOnList = pidList.Front(); nil != pidOnList; pidOnList = pidOnList.Next() { 764 pid, ok = pidOnList.Value.(int) 765 if !ok { 766 log.Fatalf("pidOnList.Value.(int) failed") 767 } 768 pidSlice = append(pidSlice, pid) 769 } 770 771 return 772 } 773 774 func pstreeStep(ppid int, pidChildrenMap map[int][]int, pidList *list.List) { 775 var ( 776 ok bool 777 pid int 778 pidChildren []int 779 ) 780 781 _ = pidList.PushBack(ppid) 782 783 pidChildren, ok = pidChildrenMap[ppid] 784 if !ok { 785 return 786 } 787 788 for _, pid = range pidChildren { 789 pstreeStep(pid, pidChildrenMap, pidList) 790 } 791 } 792 793 func pidSliceEqual(pidSlice1 []int, pidSlice2 []int) (equal bool) { 794 var ( 795 ok bool 796 pid int 797 pidSlice1Map map[int]struct{} // Go's version of a "set" 798 ) 799 800 if len(pidSlice1) != len(pidSlice2) { 801 equal = false 802 return 803 } 804 805 pidSlice1Map = make(map[int]struct{}) 806 807 for _, pid = range pidSlice1 { 808 pidSlice1Map[pid] = struct{}{} 809 } 810 811 for _, pid = range pidSlice2 { 812 _, ok = pidSlice1Map[pid] 813 if !ok { 814 equal = false 815 return 816 } 817 } 818 819 equal = true 820 return 821 } 822 823 func cleanDirectoryUnderFUSEMountPointName() { 824 var ( 825 dir *os.File 826 err error 827 name string 828 nameJoined string 829 names []string 830 umountCmd *exec.Cmd 831 ) 832 833 for { 834 time.Sleep(openDirPollDelay) 835 836 dir, err = os.Open(fuseMountPointName) 837 if nil == err { 838 break 839 } 840 841 log.Printf("Retrying os.Open(\"%v\") after \"fusermount -u\" due to err == %v", fuseMountPointName, err) 842 843 umountCmd = exec.Command("fusermount", "-u", fuseMountPointName) 844 845 err = umountCmd.Run() 846 if nil != err { 847 log.Printf("umountCmd.Run() failed: %v", err) 848 } 849 } 850 851 names, err = dir.Readdirnames(-1) 852 if nil != err { 853 _ = dir.Close() 854 log.Fatalf("dir.Readdirnames(-1) failed: %v", err) 855 } 856 857 err = dir.Close() 858 if nil != err { 859 log.Fatalf("dir.Close() failed: %v", err) 860 } 861 862 for _, name = range names { 863 nameJoined = filepath.Join(fuseMountPointName, name) 864 err = os.RemoveAll(nameJoined) 865 if nil != err { 866 log.Fatalf("os.RemoveAll(%v) failed: %v", nameJoined, err) 867 } 868 } 869 }