1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 
<?php
    declare(strict_types=1);
    /**
     *  +------------------------------------------------------------+
     *  | apnscp                                                     |
     *  +------------------------------------------------------------+
     *  | Copyright (c) Apis Networks                                |
     *  +------------------------------------------------------------+
     *  | Licensed under Artistic License 2.0                        |
     *  +------------------------------------------------------------+
     *  | Author: Matt Saladna (msaladna@apisnetworks.com)           |
     *  +------------------------------------------------------------+
     */

    use Frontend\Css\StyleManager;
    use Lararia\Bootstrapper;
    use Lararia\JobDaemon;
    use Laravel\Horizon\Contracts\JobRepository;
    use Opcenter\Apnscp;
    use Opcenter\Map;
    use Opcenter\System\Memory;

    /**
     * Miscellaneous functions that just don't have a place elsewhere
     *
     * @package core
     */
    class Misc_Module extends Module_Skeleton
    {
        const MOUNTRC = '/etc/init.d/vmount';
        const MEMTEST_KEY = '_misc_cron_memory_test';
        const MOUNTABLE_SERVICES = [
            'procfs', 'fcgi'
        ];
        protected $exportedFunctions =
            [
                '*'                      => PRIVILEGE_SITE,
                'get_job_queue'          => PRIVILEGE_ADMIN,
                'jobify'                 => PRIVILEGE_ADMIN,
                'flush_cp_version'       => PRIVILEGE_ADMIN,
                'cp_version'             => PRIVILEGE_ALL,
                'platform_version'       => PRIVILEGE_ALL,
                'dashboard_memory_usage' => PRIVILEGE_ALL,
                'lservice_memory_usage'  => PRIVILEGE_ALL,
                'changelog'              => PRIVILEGE_ALL,
                'run'                    => PRIVILEGE_SITE,
                'notify_installed'       => PRIVILEGE_ADMIN,
                'notify_update_failure'  => PRIVILEGE_ADMIN,
                'list_commands'          => PRIVILEGE_ALL,
                'command_info'           => PRIVILEGE_ALL,
                'debug_session'          => PRIVILEGE_ADMIN,
                'theme_inventory'        => PRIVILEGE_ADMIN,
                // wrappers for list_commands, command_info
                'i'                      => PRIVILEGE_ALL,
                'l'                      => PRIVILEGE_ALL
            ];

        /**
         * Current control panel version
         *
         * @param string $field
         * @return array|string
         */
        public function cp_version(string $field = '')
        {
            return \Opcenter::versionData($field);
        }

        /**
         * Force recheck on next cp_version query
         *
         * @return bool
         */
        public function flush_cp_version(): bool
        {
            return Opcenter::forgetVersion();
        }

        /**
         * Get platform version
         *
         * @return string
         */
        public function platform_version(): string
        {
            return platform_version();
        }

        /**
         * int dashboard_memory_usage()
         *
         * @return int memory usage, in bytes, that the dashboard is currently
         * consuming
         */
        public function dashboard_memory_usage(): int
        {
            return memory_get_usage();
        }

        /**
         * int lservice_memory_usage()
         *
         * @return int memory usage in bytes
         */
        public function apnscpd_memory_usage(): int
        {
            if (!IS_CLI) {
                return $this->query('misc_apnscpd_memory_usage');
            }

            return memory_get_usage();
        }

        /**
         * Toggle procfs presence
         *
         * @return bool
         */
        public function toggle_procfs(): bool
        {
            if (!$this->getServiceValue('ssh', 'enabled')) {
                return error('procfs requires ssh');
            }
            if ($this->is_mounted('procfs')) {
                return $this->unmount_service('procfs');
            }

            return $this->mount_service('procfs');
        }

        /**
         * Service is mounted
         *
         * @param string $svc
         * @return bool
         */
        public function is_mounted(string $svc): bool
        {
            if (!\in_array($svc, static::MOUNTABLE_SERVICES, true)) {
                return error("Unknown service `%s'", $svc);
            }
            // helios & apollo automatically mount fcgi
            if (version_compare(platform_version(), '6', '>=')) {
                // sol automatically mounts procfs
                return true;
            }
            $proc = Util_Process::exec('%s mounted %s %s',
                self::MOUNTRC,
                $this->site,
                $svc,
                array(0, 1)
            );

            return $proc['return'] === 0;
        }

        /**
         * Unmount service from site
         *
         * @param string $svc
         * @return bool
         */
        public function unmount_service(string $svc): bool
        {
            if (!\in_array($svc, static::MOUNTABLE_SERVICES, true)) {
                return error("Unknown service `%s'", $svc);
            }
            // helios & apollo automatically mount fcgi
            if ($svc == 'procfs' && version_compare(platform_version(), '6', '>=')) {
                return true;
            }

            if (!IS_CLI) {
                return $this->query('misc_unmount_service', $svc);
            }
            $proc = Util_Process::exec(
                '%s unmount %s %s',
                self::MOUNTRC,
                $this->site,
                $svc
            );
            if ($proc['errno'] != 0) {
                return false;
            }

            return $this->_edit_mount_map($svc, false) !== 0;
        }

        /**
         * Update internal mount map
         *
         * @param string $svc
         * @param bool $mount
         * @return int
         */
        private function _edit_mount_map(string $svc, bool $mount): int
        {
            $sysconf = '/etc/sysconfig/vmount-' . $svc;
            touch($sysconf);
            $sites = explode("\n", trim(file_get_contents($sysconf)));
            $idx = array_search($this->site, $sites, true);
            if ($mount && $idx === false) {
                $sites[] = $this->site;
            } else if (!$mount && $idx !== false) {
                unset($sites[$idx]);
            } else {
                return -1;
            }
            file_put_contents($sysconf, join("\n", $sites));

            return 1;
        }

        /**
         * Mount service
         *
         * @param string $svc
         * @return bool
         */
        public function mount_service($svc): bool
        {
            if (!\in_array($svc, static::MOUNTABLE_SERVICES, true)) {
                return error("Unknown service `%s'", $svc);
            }
            // helios & apollo automatically mount fcgi
            if ($svc == 'fcgi' && version_compare(platform_version(), '4.5', '>=')) {
                return true;
            }
            if ($svc == 'procfs' && version_compare(platform_version(), '6', '>=')) {
                return true;
            }
            if (!IS_CLI) {
                return $this->query('misc_mount_service', $svc);
            }
            $proc = Util_Process::exec(
                '%s mount %s %s',
                self::MOUNTRC,
                $this->site,
                $svc
            );
            if ($proc['return'] !== 0) {
                return false;
            }

            return $this->_edit_mount_map($svc, true) !== 0;
        }

        /**
         * procfs is mounted
         *
         * @return bool
         */
        public function procfs_enabled(): bool
        {
            return $this->is_mounted('procfs');
        }

        /**
         * Get changelog
         *
         * @return array
         */
        public function changelog(): array
        {
            $cache = \Cache_Global::spawn();
            $key = 'misc.changelog';
            $changelog = $cache->get($key);
            if ($changelog) {
                return $changelog;
            }

            $proc = Util_Process::exec('cd ' . INCLUDE_PATH . ' && git log --submodule -n 15 ');
            if (!$proc['success']) {
                return [];
            }
            $res = [];
            preg_match_all(Regex::CHANGELOG_COMMIT, $proc['output'], $matches, PREG_SET_ORDER);
            foreach ($matches as $match) {
                foreach (array_keys($match) as $key) {
                    if (is_numeric($key)) {
                        unset($match[$key]);
                    } else if ($key === 'msg') {
                        $match[$key] = trim($match[$key]);
                    } else if ($key === 'date') {
                        // rename to ts for more appropriate data type
                        $match['ts'] = strtotime($match[$key]);
                        unset($match[$key]);
                    }
                }
                $res[] = $match;
            }
            $cache->set($key, $res);

            return $res;
        }

        /**
         * Notify admin panel has been installed
         *
         * @param string $password
         * @return bool
         */
        public function notify_installed(string $password): bool
        {
            if (!($email = $this->admin_get_email())) {
                return error('Cannot send notification email - no email defined! See docs/INSTALL.md');
            }

            $mail = Illuminate\Support\Facades\Mail::to($email);
            $args = [
                'hostname'       => SERVER_NAME,
                'admin_user'     => $this->username,
                'admin_password' => $password,
                'apnscp_root'    => INCLUDE_PATH,
                'ip'             => \Opcenter\Net\Ip4::my_ip()
            ];
            $mail->send(new \Lararia\Mail\PanelInstalled($args));

            return true;
        }

        /**
         * Scan for update failure notifying admin
         *
         * @return bool
         */
        public function notify_update_failure(): bool
        {
            // @TODO crm_notify() support

            if (!($email = $this->admin_get_email())) {
                return error('Cannot send notification email - no email defined! See docs/INSTALL.md');
            }

            if (!file_exists($path = storage_path('.upcp.failure'))) {
                return true;
            }

            $mail = Illuminate\Support\Facades\Mail::to($email);
            $msg = (new \Lararia\Mail\Simple('email.admin.update-failed'))
                ->asMarkdown()->attach($path, ['as' => 'update-log.txt']);
            $mail->send($msg);
            unlink($path);
            return true;
        }

        /**
         * Get all available module commands
         *
         * @param string $filter optional filter following glob-style rules
         * @return array
         */
        public function list_commands(string $filter = ''): array
        {
            $fns = [];
            $modules = \apnscpFunctionInterceptor::list_all_modules();
            asort($modules);
            foreach ($modules as $module) {
                $moduleFns = $this->getApnscpFunctionInterceptor()->authorized_functions($module);
                asort($moduleFns);
                if ($filter) {
                    $moduleFns = array_filter($moduleFns, static function ($fn) use ($filter, $module) {
                        return fnmatch($filter, "${module}_${fn}")
                            || fnmatch($filter, "$module:" . str_replace('_', '-', $fn));
                    });
                }
                $fns[$module] = array_values($moduleFns);
            }

            return array_filter($fns);
        }

        /**
         * Enable debugging for a frontend session
         *
         * @param string $id    session ID
         * @param bool   $state debug state to set
         * @return bool
         */
        public function debug_session(string $id, bool $state = true): bool
        {
            if (!is_debug()) {
                return error('%s may only be called when debug mode is enabled', __FUNCTION__);
            }
            if (!apnscpSession::init()->exists($id)) {
                return error('Session %s does not exist', $id);
            }

            if (!$old = session_id()) {
                fatal('???');
            }

            if (extension_loaded('pcntl')) {
                $asyncEnabled = pcntl_async_signals(false);
            }
            $oldId = \session_id();
            if (!apnscpSession::restore_from_id($id, false)) {
                fatal('Unable to restore session');
            }

            Session::set('DEBUG', $state);

            if (!apnscpSession::restore_from_id($oldId, false)) {
                fatal('Failed to revert session');
            }

            if (extension_loaded('pcntl')) {
                pcntl_signal_dispatch();
                pcntl_async_signals($asyncEnabled);
            }

            return true;
        }

        /**
         * Get command information
         *
         * @param string $filter
         * @return array single or multi keyed by name => [doc, parameters, min, max, return, signature]
         */
        public function command_info(string $filter = ''): array
        {
            $fns = $this->list_commands($filter);
            if (!$fns) {
                return [];
            }
            $info = [];

            foreach ($fns as $module => $moduleFunctions) {
                $class = apnscpFunctionInterceptor::get_autoload_class_from_module($module);
                $instance = $class::autoloadModule($this->getAuthContext());
                try {
                    $rfxn = new ReflectionClass($instance);
                } catch (ReflectionException $e) {
                    debug("Failed to reflect class `%s': %s", $class, $e->getMessage());
                    continue;
                }
                foreach ($moduleFunctions as $fn) {
                    try {
                        $rfxnMethod = $rfxn->getMethod($fn);
                    } catch (ReflectionException $e) {
                        debug("Failed to reflect `%s'::`%s': %s",  $module, $fn, $e->getMessage());
                        continue;
                    }
                    $signature = "${module}_${fn}(";
                    $args = [];
                    foreach ($rfxnMethod->getParameters() as $param) {
                        $parameterSignature = '';
                        if ($param->isOptional()) {
                            $parameterSignature .= '[';
                        }
                        if ($param->getType()) {
                            $parameterSignature .= $param->getType()->getName() . ' ';
                        }
                        $parameterSignature .= '$' . $param->getName();
                        $args[] = $parameterSignature;
                    }
                    $signature .= implode(',', $args) .
                        str_repeat(
                            ']',
                            $rfxnMethod->getNumberOfParameters() - $rfxnMethod->getNumberOfRequiredParameters()
                        ) . ')';
                    $args = [
                        'doc' => preg_replace('/^\s+/m', '', $rfxnMethod->getDocComment()),
                        'parameters' => array_map('\strval', $rfxnMethod->getParameters()),
                        'min' => $rfxnMethod->getNumberOfRequiredParameters(),
                        'max' => $rfxnMethod->getNumberOfParameters(),
                        'return' => $rfxnMethod->getReturnType()->getName(),
                        'signature' => $signature
                    ];
                    $info["${module}_${fn}"] = $args;
                }
            }

            if (\count($info) === 1) {
                return array_pop($info);
            }

            return $info;
        }


        /**
         * Wrapper for list_commands
         *
         * @param string $filter
         * @return array
         */
        public function l(string $filter = ''): array
        {
            return $this->list_commands($filter);
        }

        /**
         * Wrapper for command_info
         *
         * @param string $filter
         * @return array
         */
        public function i(string $filter = ''): array
        {
            return $this->command_info($filter);
        }

        /**
         * Get pending/running job queue
         *
         * @return array
         */
        public function get_job_queue(): array
        {
            $app = \Lararia\Bootstrapper::minstrap();
            $jobs = $app->make(JobRepository::class);
            if (!$jobs) {
                return [];
            }

            return $jobs->getRecent()->map(static function ($job) {
                $payload = json_decode((string)$job->payload, true);
                $job->tag = (array)array_get((array)$payload, 'tags', []);
                $job->payload = null;
                return $job;
            })->filter(static function ($job) {
                return !$job->completed_at && !$job->failed_at && $job->status;
            })->values()->toArray();
        }

        /**
         * Run command as a job
         *
         * @param string      $cmd
         * @param array       $args
         * @param string|null $site optional site to run as
         * @return bool
         */
        public function jobify(string $cmd, array $args = [], string $site = null): bool
        {
            $context = \Auth::context(null, $site);
            $job = \Lararia\Jobs\Job::create(
                \Lararia\Jobs\SimpleCommandJob::class,
                $context,
                $cmd,
                ...$args
            );
            $job->setTags([$context->site, $cmd]);
            $job->dispatch();

            return true;
        }


        public function _edit()
        {
            $conf_old = $this->getAuthContext()->getAccount()->old;
            $conf_new = $this->getAuthContext()->getAccount()->new;
            if ($conf_new == $conf_old) {
                return;
            }
            if (!$conf_new['ssh']['enabled']) {
                $this->_delete();
            }

            return;
        }

        public function _delete()
        {
            $services = array('procfs', 'fcgi');
            foreach ($services as $s) {
                if ($this->is_mounted($s)) {
                    $this->unmount_service($s);
                }
            }
        }

        public function _cron(Cronus $cron) {
            static $cfg;

            \Opcenter\Http\Apnscp::cull();

            if (null === $cfg) {
                $cfg = [
                    'maxmemory' => Memory::stats()['memtotal'] . 'KB'
                ];
                foreach (['redis.conf'] as $f) {
                    $path = config_path($f);
                    if (!file_exists($path)) {
                        continue;
                    }
                    $cfg = Map::load($path, 'r', 'textfile')->fetchAll() + $cfg;
                }
                $cfg['maxmemory'] = Formatter::changeBytes($cfg['maxmemory']);
            }

            $cache = \Cache_Global::spawn();
            $stats = $cache->info();

            if ($stats['used_memory'] < ($cfg['maxmemory'] * 0.995 /* crit limit */)) {
                return;
            }
            // reclaimable entries may be purged to push a storage through, emulate the request to verify
            try {
                $cache->set(self::MEMTEST_KEY, str_repeat('X', $cfg['maxmemory']-$stats['used_memory']+2), 1);
            } catch (RedisException $e) {
                warn("Redis memory usage `%.2f' MB within maxmemory `%.2f' MB - raising by 20%%",
                    Formatter::changeBytes($stats['used_memory'], 'MB', 'B'),
                    Formatter::changeBytes($cfg['maxmemory'], 'MB', 'B')
                );
                $path = config_path('redis.conf');
                $cfg = Map::load($path, 'r+', 'textfile');
                $cfg['maxmemory'] = (int)(Formatter::changeBytes($stats['maxmemory'], 'MB', 'B') * 1.2) . 'MB';
                $cfg->save();
                silence(static function () use ($cache) {
                    JobDaemon::get()->running() && JobDaemon::get()->kill();
                    try {
                        // a shutdown will abruptly exit
                        $cache->rawCommand('SHUTDOWN');
                    } catch (RedisException $e) {
                    }
                    unset($cache);
                    Apnscp::restart('now');
                    exit;
                });
            } finally {
                $cache->del(self::MEMTEST_KEY);
            }

            if (!APNSCPD_HEADLESS && !\Opcenter\License::get()->isDnsOnly()) {
                $cron->schedule(86400*5, 'theme', function () {
                    $this->theme_inventory();
                });
            }
        }

        public function theme_inventory() {
            $site = \Opcenter\Account\Ephemeral::create();
            $driver = new \Service\BulkCapture(new \Service\CaptureDevices\Chromedriver);
            $ctx = $site->getContext();
            $afi = $site->getApnscpFunctionInterceptor();
            $id = $this->admin_hijack($ctx->site, null, 'UI');
            debug("Setting id: %s", $id);
            $prefs = $afi->common_load_preferences();
            foreach (StyleManager::getThemes() as $theme) {
                array_set($prefs, Page_Renderer::THEME_KEY, $theme);
                $afi->common_save_preferences($prefs);
                debug('Capturing theme %s on %s', $theme, $ctx->site);
                $driver->snap(\Opcenter\Http\Apnscp::CHECK_URL, '/apps/dashboard?' . session_name() . '=' . $id, null, storage_path('themes/' . $theme . '.png'));
            }
        }

        public function _housekeeping()
        {
            // flush cp pagespeed cache
            if (extension_loaded('curl')) {
                $adapter = new HTTP_Request2_Adapter_Curl();
            } else {
                $adapter = new HTTP_Request2_Adapter_Socket();
            }
            if (!APNSCPD_HEADLESS) {
                dlog('Purging CP pagespeed cache');
                $url = 'http://localhost:' . Auth_Redirect::CP_PORT . '/*';

                $http = new HTTP_Request2(
                    $url,
                    'PURGE',
                    array(
                        'adapter'         => $adapter,
                        'store_body'      => false,
                        'timeout'         => 5,
                        'connect_timeout' => 3
                    )
                );
                try {
                    $http->send();
                } catch (Exception $e) {
                    dlog("WARN: failed to purge pagespeed cache, %s. Is `%s' reachable?",
                        $e->getMessage(),
                        dirname($url));
                }
            }

            $ret = \Util_Process::exec('%s/artisan config:cache', INCLUDE_PATH);
            if ($ret['success']) {
                dlog('Cached Laravel configuration');
            } else {
                dlog('Failed to cache Laravel configuration - %s', coalesce($ret['stderr'], $ret['stdout']));
            }
            $path = Bootstrapper::app()->getCachedConfigPath();
            if (file_exists($path) && filesize($path) === 0) {
                dlog("Removing zero-byte cached configuration in `%s'", $path);
                unlink($path);
            }

            dlog('Updating browscap');

            \Util_Browscap::update();

            if (Opcenter::updateTags()) {
                dlog('Release tags updated');
            }

            dlog('Rewriting AOF data');
            try {
                /**
                 * Close connection once BGREWRITEAOF command is sent.
                 * Failure to close results in desynchronous results getting sent back
                 * such as BGREWRITEAOF status or incorrect GETs
                 */
                if (!Cache_Global::spawn()->bgrewriteaof()) {
                    throw new \RedisException('Failed to perform bgrewrite operation');
                }
                Cache_Base::disconnect();
            } catch (\RedisException $e) {
                warn('Failed to rewrite AOF');
            }

            return true;
        }
    }