Ejemplo n.º 1
0
    def mount(self, passwd=None):
        """
        Mount partition.

        :param str passwd: If disk is encrypted, use this passphrase to unlock
        """
        if self.mountpoint and os.path.ismount(self.mountpoint):
            raise errors.InvalidConfigError("Virtual disk already mounted")
        signals.emit("filesystems", "pre_mount", self)
        if not os.path.isdir(os.path.join("/media", self.id)):
            os.makedirs(os.path.join("/media", self.id))
        mount_point = self.mountpoint or os.path.join("/media", self.id)
        luks_point = os.path.join("/dev/mapper", self.id)
        # Find a free loopback device and mount
        loop = losetup.find_unused_loop_device()
        loop.mount(str(self.path), offset=1048576)
        if self.crypt and passwd:
            # If it's an encrypted virtual disk, decrypt first then mount
            s = crypto.luks_open(loop.device, self.id, passwd)
            if s != 0:
                loop.unmount()
                excmsg = "Failed to decrypt {0} with errno {1}"
                raise errors.OperationFailedError(
                    excmsg.format(self.id, str(s)))
            s = libc.mount(ctypes.c_char_p(b(luks_point)),
                           ctypes.c_char_p(b(mount_point)),
                           ctypes.c_char_p(b(self.fstype)), 0,
                           ctypes.c_char_p(b""))
            if s == -1:
                crypto.luks_close(self.id)
                loop.unmount()
                excmsg = "Failed to mount {0}: {1}"
                raise errors.OperationFailedError(
                    excmsg.format(self.id, os.strerror(ctypes.get_errno())))
        elif self.crypt and not passwd:
            excstr = "Must provide password to decrypt encrypted container"
            raise errors.InvalidConfigError(excstr)
        else:
            s = libc.mount(ctypes.c_char_p(b(loop.device)),
                           ctypes.c_char_p(b(mount_point)),
                           ctypes.c_char_p(b(self.fstype)), 0,
                           ctypes.c_char_p(b""))
            if s == -1:
                loop.unmount()
                excstr = "Failed to mount {0}: {1}"
                raise errors.OperationFailedError(
                    excmsg.format(self.id, os.strerror(ctypes.get_errno())))
        signals.emit("filesystems", "post_mount", self)
        self.mountpoint = mount_point
Ejemplo n.º 2
0
Archivo: php.py Proyecto: ns408/core
def verify_composer():
    """Verify Composer installation status."""
    if not find_executable("composer"):
        install_composer()
    if not find_executable("composer"):
        logmsg = "Composer installation failed."
        raise errors.OperationFailedError(logmsg)
Ejemplo n.º 3
0
    def post_install(self, extra_vars, dbpasswd=""):

        # Make sure the webapps config points to
        # the _site directory and generate it.
        c = nginx.loadf(os.path.join('/etc/nginx/sites-available', self.id))
        for x in c.servers:
            if x.filter('Key', 'root'):
                x.filter('Key', 'root')[0].value = \
                    os.path.join(self.path, '_site')
        nginx.dumpf(c, os.path.join('/etc/nginx/sites-available', self.id))
        s = shell('jekyll build --source {0} --destination {1}'.format(
            self.path, os.path.join(self.path, '_site')))
        if s["code"] != 0:
            raise errors.OperationFailedError(
                'Jekyll failed to build: {0}'.format(str(s["stderr"])))
        uid, gid = users.get_system("http").uid, groups.get_system("http").gid
        for r, d, f in os.walk(self.path):
            for x in d:
                os.chmod(os.path.join(r, x), 0o755)
                os.chown(os.path.join(r, x), uid, gid)
            for x in f:
                os.chmod(os.path.join(r, x), 0o644)
                os.chown(os.path.join(r, x), uid, gid)

        # Return an explicatory message.
        return 'Jekyll has been setup, with a sample site at {0}. '\
            'Modify these files as you like. To learn how to use Jekyll, '\
            'visit http://jekyllrb.com/docs/usage. After making changes, '\
            'click the site icon to edit, then "Regenerate Site" '\
            'to bring your changes live.'.format(self.path)
Ejemplo n.º 4
0
 def disable(self):
     """Disable connection on boot."""
     s = shell("netctl disable {0}".format(self.id))
     if s["code"] == 0:
         self.enabled = False
     else:
         raise errors.OperationFailedError("Network disable failed")
Ejemplo n.º 5
0
Archivo: nodejs.py Proyecto: ns408/core
def install(*mods, **kwargs):
    """
    Install a set of NPM packages.

    Include ``as_global`` in kwargs to install package as global.

    :param *mods: Packages to install
    :param **kwargs: Extra keyword arguments to pass to NPM
    """
    as_global = "-g " if kwargs.get("as_global") else ""
    mods = " ".join(x for x in mods)
    opt_str = ""
    if kwargs.get("opts"):
        opt_str += " --"
    for k, v in kwargs.get("opts", {}):
        opt_str += " --".join(k + v if v[0] == '=' else k + " " + v)
    cwd = os.getcwd()
    if "install_path" in kwargs:
        os.chdir(kwargs["install_path"])
    s = shell("npm install {0}{1}{2}".format(
        as_global,
        mods,
    ))
    os.chdir(cwd)
    if s["code"] != 0:
        errmsg = "NPM install of {0} failed.".format(mods)
        logmsg = "NPM install failure details:\n{0}"
        logger.error("NodeJS", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 6
0
    def post_install(self, extra_vars, dbpasswd=""):
        secret_key = random_string()
        dbengine = 'mysql' \
            if self.app.selected_dbengine == 'db-mariadb' \
            else 'sqlite'

        # Write a standard Wallabag config file
        config_file = os.path.join(self.path, 'app/config/parameters.yml')
        with open(config_file + ".dist", 'r') as f:
            ic = f.readlines()
        with open(config_file, 'w') as f:
            for l in ic:
                if "database_driver: " in l:
                    pdo = "pdo_mysql" if dbengine == "mysql" else "pdo_sqlite"
                    l = "    database_driver: {0}\n".format(pdo)
                elif "database_path: " in l and dbengine == 'sqlite':
                    l = "    database_path: {0}\n".format(self.db.path)
                elif "database_name: " in l and dbengine == 'mysql':
                    l = "    database_name: {0}\n".format(self.db.id)
                elif "database_user: "******"    database_user: {0}\n".format(self.db.id)
                elif "database_password: "******"{0}"\n'.format(dbpasswd)
                elif "secret: " in l:
                    l = "    secret: {0}\n".format(secret_key)
                f.write(l)

        # Make sure that the correct PHP settings are enabled
        php.enable_mod('sqlite3', 'bcmath',
                       'pdo_mysql' if dbengine == 'mysql' else 'pdo_sqlite',
                       'zip', 'tidy')
        php.open_basedir('add', '/usr/bin/php')

        uid, gid = users.get_system("http").uid, groups.get_system("http").gid

        # Set up the database then delete the install folder
        if dbengine == 'sqlite3':
            php.open_basedir('add', '/var/lib/sqlite3')

        cwd = os.getcwd()
        os.chdir(self.path)
        s = shell("php bin/console wallabag:install --env=prod -n")
        if s["code"] != 0:
            logger.error("Websites", s["stderr"].decode())
            raise errors.OperationFailedError(
                "Failed to populate database. See logs for more info")
        os.chdir(cwd)

        if dbengine == 'sqlite3':
            os.chown("/var/lib/sqlite3/{0}.db".format(self.db.id), -1, gid)
            os.chmod("/var/lib/sqlite3/{0}.db".format(self.db.id), 0o660)

        # Finally, make sure that permissions are set so that Wallabag
        # can make adjustments and save plugins when need be.
        for r, d, f in os.walk(self.path):
            for x in d:
                os.chown(os.path.join(r, x), uid, gid)
            for x in f:
                os.chown(os.path.join(r, x), uid, gid)
Ejemplo n.º 7
0
    def mount(self, passwd=None):
        """
        Mount partition.

        :param str passwd: If disk is encrypted, use this passphrase to unlock
        """
        if self.mountpoint and os.path.ismount(self.mountpoint):
            raise errors.InvalidConfigError("Disk partition already mounted")
        elif self.fstype == "Unknown":
            emsg = "Cannot mount a partition of unknown type"
            raise errors.InvalidConfigError(emsg)
        signals.emit("filesystems", "pre_mount", self)
        mount_point = self.mountpoint or os.path.join("/media", self.id)
        luks_point = os.path.join("/dev/mapper", self.id)
        if not os.path.isdir(mount_point):
            os.makedirs(mount_point)
        if self.crypt and passwd:
            # Decrypt the disk first if it's an encrypted disk
            s = crypto.luks_open(self.path, self.id, passwd)
            if s != 0:
                excmsg = "Failed to decrypt {0} with errno {1}"
                excmsg = excmsg.format(self.id, str(s))
                raise errors.OperationFailedError(excmsg)
            s = libc.mount(ctypes.c_char_p(b(luks_point)),
                           ctypes.c_char_p(b(mount_point)),
                           ctypes.c_char_p(b(self.fstype)), 0,
                           ctypes.c_char_p(b""))
            if s == -1:
                crypto.luks_close(self.id)
                excmsg = "Failed to mount {0}: {1}"
                raise errors.OperationFailedError(
                    excmsg.format(self.id, os.strerror(ctypes.get_errno())))
        elif self.crypt and not passwd:
            emsg = "Must provide password to decrypt encrypted disk"
            raise errors.InvalidConfigError(emsg)
        else:
            s = libc.mount(ctypes.c_char_p(b(self.path)),
                           ctypes.c_char_p(b(mount_point)),
                           ctypes.c_char_p(b(self.fstype)), 0,
                           ctypes.c_char_p(b""))
            if s == -1:
                excmsg = "Failed to mount {0}: {1}"
                raise errors.OperationFailedError(
                    excmsg.format(self.id, os.strerror(ctypes.get_errno())))
        signals.emit("filesystems", "post_mount", self)
        self.mountpoint = mount_point
Ejemplo n.º 8
0
 def disconnect(self):
     """Disconnect from network."""
     signals.emit("networks", "pre_disconnect", self)
     s = shell("netctl stop {0}".format(self.id))
     if s["code"] == 0:
         self.connected = False
         signals.emit("networks", "post_disconnect", self)
     else:
         raise errors.OperationFailedError("Network disconnection failed")
Ejemplo n.º 9
0
    def encrypt(self,
                passwd,
                cipher=config.get("filesystems", "cipher"),
                keysize=config.get("filesystems", "keysize"),
                mount=False):
        """
        Encrypt virtual disk image.

        :params str passwd: Passphrase to encrypt disk with
        :params str cipher: cipher suite to use (default aes-xts-plain64)
        :params str keysize: default key size to use (default 256)
        :params bool mount: mount after encrypt?
        """
        cipher = cipher or "aes-xts-plain64"
        keysize = keysize or 256
        vdisk_dir = config.get("filesystems", "vdisk_dir")
        os.rename(self.path, os.path.join(vdisk_dir, self.id + ".crypt"))
        self.path = os.path.join(vdisk_dir, self.id + ".crypt")
        # Find an open loopback device and mount
        loop = losetup.find_unused_loop_device()
        loop.mount(str(self.path), offset=1048576)
        # Encrypt the file inside the loopback and mount
        s = crypto.luks_format(loop.device, passwd, cipher, int(keysize))
        if s != 0:
            loop.unmount()
            os.rename(self.path, os.path.join(vdisk_dir, self.id + ".img"))
            excstr = "Failed to encrypt {0} with errno {1}"
            raise errors.OperationFailedError(excstr.format(self.id, str(s)))
        s = crypto.luks_open(loop.device, self.id, passwd)
        if s != 0:
            loop.unmount()
            excstr = "Failed to decrypt {0} with errno {1}"
            raise errors.OperationFailedError(excstr.format(self.id, str(s)))
        # Create a filesystem inside the encrypted device
        s = shell("mkfs.ext4 /dev/mapper/{0}".format(self.id))
        crypto.luks_close(self.id)
        loop.unmount()
        if s["code"] != 0:
            excstr = "Failed to format loop device: {0}"
            raise errors.OperationFailedError(excstr.format(s["stderr"]))
        self.crypt = True
        if mount:
            self.mount(passwd)
Ejemplo n.º 10
0
 def add_db(self):
     if re.search('\.|-|`|\\\\|\/|[ ]', self.id):
         raise errors.InvalidConfigError(
             'Name must not contain spaces, dots, dashes or other '
             'special characters')
     self.manager.chkpath()
     status = shell("sqlite3 {0} \"ATTACH '{1}' AS {2};\"".format(
         self.path, self.path, self.id))
     if status["code"] >= 1:
         raise errors.OperationFailedError(status["stderr"])
Ejemplo n.º 11
0
Archivo: backup.py Proyecto: ns408/core
 def pre_backup(self):
     """Reimplement."""
     s = shell("slapcat -n 1")
     if s["code"] != 0:
         emsg = ("Could not backup LDAP database. "
                 "Please check logs for errors.")
         logger.error("Backup", s["stderr"].decode())
         raise errors.OperationFailedError(emsg)
     with open("/tmp/ldap.ldif", "wb") as f:
         f.write(s["stdout"])
Ejemplo n.º 12
0
Archivo: backup.py Proyecto: ns408/core
 def post_restore(self):
     """Reimplement."""
     if not os.path.exists("/tmp/ldap.ldif"):
         emsg = ("Could not backup LDAP database. "
                 "Please check logs for errors.")
         logger.error("Backup", "/tmp/ldap.ldif not found")
         raise errors.OperationFailedError(emsg)
     with open("/tmp/ldap.ldif", "r") as f:
         ldif = f.read()
     s = shell('ldapadd -D "cn=admin,dc=arkos-servers,dc=org" -w {0}'
               .format(secrets.get("ldap")),
               stdin=ldif)
     if os.path.exists("/tmp/ldap.ldif"):
         os.unlink("/tmp/ldap.ldif")
     if s["code"] != 0:
         emsg = ("Could not restore LDAP database. "
                 "Please check logs for errors.")
         logger.error("Backup", s["stderr"].decode())
         raise errors.OperationFailedError(emsg)
Ejemplo n.º 13
0
 def connect(self):
     """Connect to network."""
     signals.emit("networks", "pre_connect", self)
     for x in get_connections(iface=self.config.get("interface")):
         x.disconnect()
     s = shell("netctl start {0}".format(self.id))
     if s["code"] == 0:
         self.connected = True
         signals.emit("networks", "post_connect", self)
     else:
         raise errors.OperationFailedError("Network connection failed")
Ejemplo n.º 14
0
Archivo: python.py Proyecto: ns408/core
def remove(*mods):
    """
    Remove a set of Python packages from the system.

    :param *mods: packages to remove
    """
    s = shell("pip uninstall {0}".format(mods))
    if s["code"] != 0:
        errmsg = "PyPI uninstall of {0} failed.".format(mods)
        logmsg = "PyPI uninstall failure details:\n{0}"
        logger.error("Python", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 15
0
Archivo: nodejs.py Proyecto: ns408/core
def remove(*mods):
    """
    Remove a set of NPM packages.

    :param *mods: Packages to remove
    """
    mods = " ".join(x for x in mods)
    s = shell("npm uninstall {0}".format(mods), stderr=True)
    if s["code"] != 0:
        errmsg = "NPM removal of {0} failed.".format(mods)
        logmsg = "NPM remove failure details:\n{0}"
        logger.error("NodeJS", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 16
0
def get_jail_config(jcfg=""):
    """
    Load fail2ban firewall jail configuration from file.

    :param str jailconf: path to jail configuration file
    :returns: jail config
    :rtype: configparser.RawConfigParser
    """
    cfg = configparser.RawConfigParser()
    if not cfg.read(jcfg or jailconf):
        emsg = "Fail2Ban config not found or not readable"
        raise errors.OperationFailedError(emsg)
    return cfg
Ejemplo n.º 17
0
 def umount(self):
     """Unmount partition."""
     signals.emit("filesystems", "pre_umount", self)
     if not self.mountpoint:
         return
     s = libc.umount2(ctypes.c_char_p(b(self.mountpoint)), 0)
     if s == -1:
         excmsg = "Failed to unmount {0}: {1}"
         raise errors.OperationFailedError(
             excmsg.format(self.id, os.strerror(ctypes.get_errno())))
     if self.crypt:
         crypto.luks_close(b(self.id))
     signals.emit("filesystems", "post_umount", self)
     self.mountpoint = None
Ejemplo n.º 18
0
def generate_dh_params(path, size=2048):
    """
    Create and save Diffie-Hellman parameters.

    :param str path: File path to save to
    :param int size: Key size
    """
    s = shell("openssl dhparam {0} -out {1}".format(size, path))
    if s["code"] != 0:
        emsg = "Failed to generate Diffie-Hellman parameters"
        logger.error("Certificates", s["stderr"].decode())
        raise errors.OperationFailedError(emsg)
    os.chown(path, -1, gid)
    os.chmod(path, 0o750)
Ejemplo n.º 19
0
    def create(self,
               mount=False,
               will_crypt=False,
               nthread=NotificationThread()):
        """
        Create virtual disk image.

        :param bool mount: Mount after creation?
        :param bool will_crypt: Will this disk be encrypted later?
        :param NotificationThread nthread: notification thread to use
        """
        nthread.title = "Creating virtual disk"

        vdisk_dir = config.get("filesystems", "vdisk_dir")
        if not os.path.exists(vdisk_dir):
            os.mkdir(vdisk_dir)
        self.path = str(os.path.join(vdisk_dir, self.id + ".img"))
        if os.path.exists(self.path):
            raise errors.InvalidConfigError("This virtual disk already exists")

        # Create an empty file matching disk size
        signals.emit("filesystems", "pre_add", self)
        msg = "Creating virtual disk..."
        nthread.update(Notification("info", "Filesystems", msg))
        with open(self.path, "wb") as f:
            written = 0
            with open("/dev/zero", "rb") as zero:
                while self.size > written:
                    written += 1024
                    f.write(zero.read(1024))

        if not will_crypt:
            # Get a free loopback device and mount
            loop = losetup.find_unused_loop_device()
            loop.mount(str(self.path), offset=1048576)
            # Make a filesystem
            msg = "Writing filesystem..."
            nthread.update(Notification("info", "Filesystems", msg))
            s = shell("mkfs.ext4 {0}".format(loop.device))
            if s["code"] != 0:
                excmsg = "Failed to format loop device: {0}"
                raise errors.OperationFailedError(excmsg.format(s["stderr"]))
            loop.unmount()
            msg = "Virtual disk created successfully"
            nthread.complete(Notification("success", "Filesystems", msg))

        signals.emit("filesystems", "post_add", self)
        if mount:
            self.mount()
Ejemplo n.º 20
0
def set_datetime(ut=0):
    """
    Set system time from provided Unix timestamp (or current time via NTP).

    :param int ut: Unix timestamp
    """
    ut = int(ut) if ut else int(get_idatetime())
    librt = ctypes.CDLL(ctypes.util.find_library("rt"), use_errno=True)
    ts = timespec()
    ts.tv_sec, ts.tv_nsec = ut, 0
    res = librt.clock_settime(0, ctypes.byref(ts))
    if res == -1:
        raise errors.OperationFailedError("Could not set time: {0}".format(
            os.strerror(ctypes.get_errno())))
    signals.emit("config", "time_changed", ut)
Ejemplo n.º 21
0
Archivo: python.py Proyecto: ns408/core
def install(pkg, version=None, py2=False):
    """
    Install a set of Python packages from PyPI.

    :param str pkg: package to install
    :param str version: If present, install this specific version
    :param bool py2: If True, install for Python 2.x instead
    """
    if version:
        pkg = pkg + "==" + version
    s = shell("pip{0} install {1}".format("2" if py2 else "", pkg))
    if s["code"] != 0:
        errmsg = "PyPI install of {0} failed.".format(pkg)
        logmsg = "PyPI install failure details:\n{0}"
        logger.error("Python", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 22
0
 def regenerate(self):
     path = self.path
     if not path.endswith("_site"):
         path = os.path.join(self.path, "_site")
     s = shell('jekyll build --source {0} --destination {1}'.format(
         self.path.split('/_site')[0], path))
     if s["code"] != 0:
         raise errors.OperationFailedError(
             'Jekyll failed to build: {0}'.format(str(s["stderr"])))
     uid, gid = users.get_system("http").uid, groups.get_system("http").gid
     for r, d, f in os.walk(self.path):
         for x in d:
             os.chmod(os.path.join(r, x), 0o755)
             os.chown(os.path.join(r, x), uid, gid)
         for x in f:
             os.chmod(os.path.join(r, x), 0o644)
             os.chown(os.path.join(r, x), uid, gid)
Ejemplo n.º 23
0
def verify_time(update=True, crit=True):
    """
    Verify system time with NTP, set it if it is more than an hour off.

    :param bool update: Update system time if it is too far off.
    :param bool crit: Raise an exception if time cannot be retrieved.
    """
    try:
        os = get_offset()
    except Exception as e:
        if crit:
            raise errors.OperationFailedError(
                "System time could not be retrieved. Error: {0}".format(e))
        else:
            return "UNKNOWN"
    if (os < -3600 or os > 3600) and update:
        set_datetime()
    return os
Ejemplo n.º 24
0
Archivo: php.py Proyecto: ns408/core
def install_composer():
    """Install Composer to the system."""
    cwd = os.getcwd()
    os.chdir("/root")
    os.environ["COMPOSER_HOME"] = "/root"
    enable_mod("phar")
    open_basedir("add", "/root")
    r = requests.get("https://getcomposer.org/installer")
    s = shell("php", stdin=r.text)
    os.chdir(cwd)
    if s["code"] != 0:
        errmsg = "Composer download/config failed."
        logmsg = "Composer install failure details:\n{0}"
        logger.error("PHP", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
    os.rename("/root/composer.phar", "/usr/local/bin/composer")
    os.chmod("/usr/local/bin/composer", 0o755)
    open_basedir("add", "/usr/local/bin")
Ejemplo n.º 25
0
def install(gem, version=None, update=False):
    """
    Install a Ruby gem to the system.

    :param str gem: Gem name
    :param str version: If present, install this specific version
    :param bool update: If true, force an update
    """
    verify_path()
    if version:
        gem = gem + ":" + version
    s = shell("gem {0} -N --no-user-install {1}".format(
        "update" if update else "install", gem))
    if s["code"] != 0:
        errmsg = "Gem install of {0} failed.".format(gem)
        logmsg = "Gem install failure details:\n{0}"
        logger.error("Ruby", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 26
0
Archivo: nodejs.py Proyecto: ns408/core
def install_from_package(path, stat="production", opts={}):
    """
    Install a set of NPM package dependencies from an NPM package.json.

    :param str path: path to directory with package.json
    :param str stat: Install to "production"?
    """
    stat = (" --" + stat) if stat else ""
    opts = (" --" + " --".join(x + "=" + y for x, y in opts)) if opts else ""
    cwd = os.getcwd()
    os.chdir(path)
    s = shell("npm install {0}{1}".format(stat, opts))
    os.chdir(cwd)
    if s["code"] != 0:
        errmsg = "NPM install of {0} failed.".format(path)
        logmsg = "NPM install failure details:\n{0}"
        logger.error("NodeJS", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 27
0
 def umount(self):
     """Unmount disk."""
     if not self.mountpoint:
         return
     signals.emit("filesystems", "pre_umount", self)
     loops = losetup.get_loop_devices()
     for l in loops:
         if loops[l].is_used() and loops[l].get_filename() == self.path:
             dev = loops[l]
             break
     s = libc.umount2(ctypes.c_char_p(b(self.mountpoint)), 0)
     if s == -1:
         excmsg = "Failed to unmount {0}: {1}"
         raise errors.OperationFailedError(
             excmsg.format(self.id, os.strerror(ctypes.get_errno())))
     if self.crypt:
         crypto.luks_close(self.id)
     if dev:
         dev.unmount()
     signals.emit("filesystems", "post_umount", self)
     self.mountpoint = None
Ejemplo n.º 28
0
Archivo: php.py Proyecto: ns408/core
def composer_install(path, flags={}, env={}):
    """
    Install a PHP application bundle via Composer.

    :param str path: path to app directory
    :param dict flags: command line flags to pass to Composer
    :param dict env: command line environment variables
    """
    verify_composer()
    cwd = os.getcwd()
    os.chdir(path)
    shell("composer self-update")
    cmdflags = []
    for x in flags:
        flg = "-{0}".format(x) if len(x) == 1 else "--{0}".format(x)
        cmdflags.append("{0}={1}".format(flg, flags[x]) if flags[x] else flg)
    s = shell("composer install {0}".format(" ".join(cmdflags)), env=env)
    os.chdir(cwd)
    if s["code"] != 0:
        errmsg = "Composer install of {0} failed.".format(path)
        logmsg = "Composer install failure details:\n{0}"
        logger.error("PHP", logmsg.format(s["stderr"].decode()))
        raise errors.OperationFailedError(errmsg)
Ejemplo n.º 29
0
 def site_edited(self):
     # Remove the existing trusted_sites array
     # then add a new one based on the new addr
     if os.path.exists(os.path.join(self.path, 'config', 'config.php')):
         path = os.path.join(self.path, 'config', 'config.php')
     else:
         raise errors.OperationFailedError(
             "Nextcloud config file not found")
     with open(path, "r") as f:
         data = f.read()
     while re.search("\n(\s*('|\")trusted_domains.*?\).*?\n)", data,
                     re.DOTALL):
         data = data.replace(
             re.search("\n(\s*('|\")trusted_domains.*?\).*?\n)", data,
                       re.DOTALL).group(1), "")
     data = data.split("\n")
     with open(path, "w") as f:
         for x in data:
             if not x.endswith("\n"):
                 x += "\n"
             if x.startswith(");"):
                 f.write("  'trusted_domains' => "
                         "array('localhost','{0}'),\n".format(self.domain))
             f.write(x)
Ejemplo n.º 30
0
    def _install(self, extra_vars, enable, nthread):
        nthread.title = "Installing website"

        msg = Notification("info", "Webs", "Preparing to install...")
        nthread.update(msg)

        # Make sure the chosen port is indeed open
        if not tracked_services.is_open_port(self.port, self.domain):
            cname = "({0})".format(self.app.id)
            raise errors.InvalidConfigError(cname, nthread)\
                from tracked_services.PortConflictError(self.port, self.domain)

        # Set some metadata values
        specialmsg, dbpasswd = "", ""
        site_dir = config.get("websites", "site_dir")
        path = (self.path or os.path.join(site_dir, self.id))
        self.path = path
        self.php = extra_vars.get("php") or self.php \
            or self.app.uses_php or False
        self.version = self.app.version.rsplit("-", 1)[0] \
            if self.app.website_updates else None

        # Classify the source package type
        if not self.app.download_url:
            ending = ""
        elif self.app.download_url.endswith(".tar.gz"):
            ending = ".tar.gz"
        elif self.app.download_url.endswith(".tgz"):
            ending = ".tgz"
        elif self.app.download_url.endswith(".tar.bz2"):
            ending = ".tar.bz2"
        elif self.app.download_url.endswith(".zip"):
            ending = ".zip"
        elif self.app.download_url.endswith(".git"):
            ending = ".git"
        else:
            raise errors.InvalidConfigError(
                "Invalid source archive format in {0}".format(self.app.id))

        msg = "Running pre-installation..."
        uid, gid = users.get_system("http").uid, groups.get_system("http").gid
        nthread.update(Notification("info", "Webs", msg))

        # Call website type's pre-install hook
        self.pre_install(extra_vars)

        # If needs DB and user didn't select an engine, choose one for them
        if len(self.app.database_engines) > 1 \
                and extra_vars.get("dbengine", None):
            self.app.selected_dbengine = extra_vars.get("dbengine")
        if not getattr(self.app, "selected_dbengine", None)\
                and self.app.database_engines:
            self.app.selected_dbengine = self.app.database_engines[0]

        # Create DB and/or DB user as necessary
        if getattr(self.app, "selected_dbengine", None):
            msg = "Creating database..."
            nthread.update(Notification("info", "Webs", msg))
            mgr = databases.get_managers(self.app.selected_dbengine)
            if not mgr:
                estr = "No manager found for {0}"
                raise errors.InvalidConfigError(
                    estr.format(self.app.selected_dbengine))
            # Make sure DB daemon is running if it has one
            if not mgr.state:
                svc = services.get(mgr.meta.database_service)
                svc.restart()
            self.db = mgr.add_db(self.id)
            if hasattr(self.db, "path"):
                os.chmod(self.db.path, 0o660)
                os.chown(self.db.path, -1, gid)
            # If multiuser DB type, create user
            if mgr.meta.database_multiuser:
                dbpasswd = random_string(16)
                db_user = mgr.add_user(self.id, dbpasswd)
                db_user.chperm("grant", self.db)

        # Make sure the target directory exists, but is empty
        pkg_path = os.path.join("/tmp", self.id + ending)
        if os.path.isdir(self.path):
            shutil.rmtree(self.path)
        os.makedirs(self.path)

        # Download and extract the source repo / package
        msg = "Downloading website source..."
        nthread.update(Notification("info", "Webs", msg))
        if self.app.download_url and ending == ".git":
            g = git.Repo.clone_from(self.app.download_url, self.path)
            if hasattr(self.app, "download_at_tag"):
                g = git.Git(self.path)
                g.checkout(self.app.download_git_tag)
        elif self.app.download_url:
            download(self.app.download_url, file=pkg_path, crit=True)

            # Format extraction command according to type
            msg = "Extracting source..."
            nthread.update(Notification("info", "Webs", msg))
            if ending in [".tar.gz", ".tgz", ".tar.bz2"]:
                arch = tarfile.open(pkg_path, "r:gz")
                r = (x for x in arch.getnames() if re.match("^[^/]*$", x))
                toplvl = next(r, None)
                if not toplvl:
                    raise errors.OperationFailedError(
                        "Malformed source archive")
                arch.extractall(site_dir)
                os.rename(os.path.join(site_dir, toplvl), self.path)
            else:
                arch = zipfile.ZipFile(pkg_path)
                r = (x for x in arch.namelist() if re.match("^[^/]*/$", x))
                toplvl = next(r, None)
                if not toplvl:
                    raise errors.OperationFailedError(
                        "Malformed source archive")
                arch.extractall(site_dir)
                os.rename(os.path.join(site_dir, toplvl.rstrip("/")),
                          self.path)
            os.remove(pkg_path)

        # Set proper starting permissions on source directory
        os.chmod(self.path, 0o755)
        os.chown(self.path, uid, gid)
        for r, d, f in os.walk(self.path):
            for x in d:
                os.chmod(os.path.join(r, x), 0o755)
                os.chown(os.path.join(r, x), uid, gid)
            for x in f:
                os.chmod(os.path.join(r, x), 0o644)
                os.chown(os.path.join(r, x), uid, gid)

        # If there is a custom path for the data directory, set it up
        if getattr(self.app, "website_datapaths", None) \
                and extra_vars.get("datadir"):
            self.data_path = extra_vars["datadir"]
            if not os.path.exists(self.data_path):
                os.makedirs(self.data_path)
            os.chmod(self.data_path, 0o755)
            os.chown(self.data_path, uid, gid)
        elif hasattr(self, "website_default_data_subdir"):
            self.data_path = os.path.join(self.path,
                                          self.website_default_data_subdir)
        else:
            self.data_path = self.path

        # Create the nginx serverblock
        addtoblock = self.addtoblock or []
        if extra_vars.get("addtoblock"):
            addtoblock += nginx.loads(extra_vars.get("addtoblock"), False)
        default_index = "index." + ("php" if self.php else "html")
        if hasattr(self.app, "website_root"):
            webroot = os.path.join(self.path, self.app.website_root)
        else:
            webroot = self.path
        block = nginx.Conf()
        server = nginx.Server(
            nginx.Key("listen", str(self.port)),
            nginx.Key("listen", "[::]:" + str(self.port)),
            nginx.Key("server_name", self.domain), nginx.Key("root", webroot),
            nginx.Key(
                "index",
                getattr(self.app, "website_index", None) or default_index),
            nginx.Location("/.well-known/acme-challenge/",
                           nginx.Key("root", self.path)))
        if addtoblock:
            server.add(*[x for x in addtoblock])
        block.add(server)
        nginx.dumpf(block, os.path.join("/etc/nginx/sites-available", self.id))
        challenge_dir = os.path.join(self.path, ".well-known/acme-challenge/")
        if not os.path.exists(challenge_dir):
            os.makedirs(challenge_dir)

        # Create arkOS metadata file
        meta = configparser.SafeConfigParser()
        meta.add_section("website")
        meta.set("website", "id", self.id)
        meta.set("website", "app", self.app.id)
        meta.set("website", "ssl",
                 self.cert.id if getattr(self, "cert", None) else "None")
        meta.set("website", "version", self.version or "None")
        if getattr(self.app, "website_datapaths", None) \
                and self.data_path:
            meta.set("website", "data_path", self.data_path)
        meta.set("website", "dbengine", "")
        meta.set("website", "dbengine",
                 getattr(self.app, "selected_dbengine", ""))
        with open(os.path.join(self.path, ".arkos"), "w") as f:
            meta.write(f)

        # Call site type's post-installation hook
        msg = "Running post-installation. This may take a few minutes..."
        nthread.update(Notification("info", "Webs", msg))
        specialmsg = self.post_install(extra_vars, dbpasswd)

        # Cleanup and reload daemons
        msg = "Finishing..."
        nthread.update(Notification("info", "Webs", msg))
        self.installed = True
        storage.websites[self.id] = self
        if self.port == 80:
            cleanup_acme_dummy(self.domain)
        signals.emit("websites", "site_installed", self)
        if enable:
            self.nginx_enable()
        if enable and self.php:
            php.open_basedir("add", "/srv/http/")
            php_reload()

        msg = "{0} site installed successfully".format(self.app.name)
        nthread.complete(Notification("success", "Webs", msg))
        if specialmsg:
            return specialmsg