def main(argv=None): dirs = appdirs.AppDirs(appname="Stud.IP-Fuse", appauthor=False) # disable author/company folder on windows os.makedirs(dirs.user_data_dir, exist_ok=True) # must exist for log files os.makedirs(dirs.user_config_dir, exist_ok=True) # must exist for oauth token storage configure_logging(dirs) args, fuse_args = parse_args(dirs, argv) os.makedirs(args.cache_dir, exist_ok=True) try: if not args.debug_logging: logging.root.setLevel(logging.INFO) if not args.debug_aio: logging.getLogger("asyncio").setLevel(logging.WARNING) log_status("STARTING", args=args, level=logging.DEBUG) log.info("Starting %s" % get_environment()) if args.debug_fuse: from studip_fuse.studipfs.fuse_ops import log_ops log_ops.setLevel(logging.DEBUG) fuse_ops = FUSEView(log_args=args, loop_setup_fn=aioimpl_asyncio.setup_loop(args=args)) if args.login_method == "oauth": login_oauth_args(args) else: if args.pwfile == "-": from getpass import getpass password = getpass() else: try: with open(args.pwfile, "rt") as f: password = f.read().rstrip('\n') except FileNotFoundError as e: log.warning("%s. Either specify a file from which your Stud.IP password can be read " "or use `--pwfile -` to enter it using a prompt in the shell." % e) return args.get_password = lambda: password # wrap in lambda to prevent printing log.info("Going to mount at %s (uid=%s, gid=%s, pid=%s, python pid=%s)", os.path.abspath(args.mount), *fuse_get_context(), os.getpid()) try: # this calls fork if args.foreground == False (and breaks running asyncio loops due to https://bugs.python.org/issue21998) # XXX on windows args.mount may not exist, on Linux it must exist FUSE(fuse_ops, args.mount, debug=fuse_args.pop("debug_fuse"), **fuse_args) except RuntimeError as e: if more_itertools.first(e.args, None) in FUSE_ERROR_CODES: msg = FUSE_ERROR_CODES[e.args[0]] if e.args[0] == 1: msg += ". Please check whether the mountpoint you specified is an empty directory or another instance of studip-fuse is using it" msg += ". Please check stderr for details." raise RuntimeError(msg) from e else: raise except SystemExit: pass except: log.error("main() function quit exceptionally", exc_info=True) finally: log_status("TERMINATED", args=args, level=logging.DEBUG) log.debug("Program terminated")
def log_status(status, args=None, suffix=tuple(), level=logging.INFO): data = (status, *fuse_get_context(), os.getpid(), args.user if args else "?", args.mount if args else "?") + suffix if status_queue: status_queue.put(data) logging.getLogger("studip_fuse.status").log(level, " ".join(["%s"] * len(data)), *data)
def symlink(self, target_path_, source_path): target_rel_path = self._rel_path(target_path_) os.symlink(source_path, target_rel_path, dir_fd = self._root_path_fd) uid, gid, pid = fuse_get_context() os.chown(target_rel_path, uid, gid, dir_fd = self._root_path_fd, follow_symlinks = False)
def mkdir(self, path, mode): rel_path = self._rel_path(path) os.mkdir(rel_path, mode, dir_fd = self._root_path_fd) uid, gid, pid = fuse_get_context() os.chown(rel_path, uid, gid, dir_fd = self._root_path_fd, follow_symlinks = False) os.chmod(rel_path, mode, dir_fd = self._root_path_fd) # follow_symlinks = False
def destroy(self, path): log_status("STOPPING", args=self.log_args) log.info("Unmounting from %s (uid=%s, gid=%s, pid=%s, python pid=%s)", path, *fuse_get_context(), os.getpid()) if self.loop_future: self.loop_future.cancel() if self.loop_stop_fn: self.loop_stop_fn() if self.loop_thread: join_thread(self.loop_thread) log.info("Unmounting complete")
def mknod(self, path, mode, dev): rel_path = self._rel_path(path) if stat.S_ISREG(mode): res = os.open( rel_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, mode, dir_fd = self._root_path_fd ) # TODO broken, applies umask to mode no matter what ... if res >= 0: os.close(res) elif stat.S_ISFIFO(mode): os.mkfifo(rel_path, mode, dir_fd = self._root_path_fd) else: os.mknod(rel_path, mode, dev, dir_fd = self._root_path_fd) uid, gid, pid = fuse_get_context() os.chown(rel_path, uid, gid, dir_fd = self._root_path_fd, follow_symlinks = False) os.chmod(rel_path, mode, dir_fd = self._root_path_fd) # follow_symlinks = False
def init(self, path): log_status("INITIALIZING", args=self.log_args) log.debug("Mounting at %s (uid=%s, gid=%s, pid=%s, python pid=%s)", path, *fuse_get_context(), os.getpid()) self.loop_future = concurrent.futures.Future() self.loop_thread = Thread(target=self.loop_setup_fn, args=(self.loop_future, ), name="aio event loop", daemon=True) self.loop_thread.start() log.debug( "Event loop thread started, waiting for initialization to complete" ) self.loop_stop_fn, self.loop_run_fn, self.root_rp = self.loop_future.result( ) log_status("READY", args=self.log_args) log.info("Mounting complete")
def _log_event_(self, func, func_arg_names, func_arg_abspath, func_arg_defaults, func_args, func_kwargs, format_pattern, ret_status, ret_value): if self._log_only_modify_operations: if func.__name__ not in ('chmod', 'chown', 'link', 'mkdir', 'mknod', 'rename', 'rmdir', 'symlink', 'truncate', 'unlink', 'utimens', 'write'): return uid, gid, pid = fuse_get_context() p_cmdname = '' if self._log_printprocessname or self._log_json: p_cmdname = _get_process_cmdline_(pid).strip() log_dict = { 'proc_cmd': p_cmdname, 'proc_uid': uid, 'proc_uid_name': _get_user_name_from_uid_(uid), 'proc_gid': gid, 'proc_gid_name': _get_group_name_from_gid_(gid), 'proc_pid': pid, 'action': func.__name__, 'status': ret_status, } arg_dict = { arg_name: arg for arg_name, arg in zip(func_arg_names, func_args) } arg_dict.update({ arg_name: func_kwargs.get(arg_name, func_arg_defaults[arg_name]) for arg_name in func_arg_names[len(func_args):] }) try: arg_dict['uid_name'] = _get_user_name_from_uid_(arg_dict['uid']) except KeyError: pass try: arg_dict['gid_name'] = _get_group_name_from_gid_(arg_dict['gid']) except KeyError: pass try: arg_dict['fip'] = _get_fh_from_fip_(arg_dict['fip']) except KeyError: pass for k in arg_dict.keys(): if k.endswith('path'): arg_dict[k] = self._full_path(arg_dict[k]) try: arg_dict['buf_len'] = len(arg_dict['buf']) arg_dict['buf'] = _encode_buffer_( arg_dict['buf']) if self._log_buffers else '' except KeyError: pass log_dict.update({'param_%s' % k: v for k, v in arg_dict.items()}) if log_dict['status']: # SUCCESS if any((ret_value is None, isinstance(ret_value, int), isinstance(ret_value, str), isinstance(ret_value, dict), isinstance(ret_value, list))): log_dict['return'] = ret_value elif isinstance(ret_value, bytes): log_dict['return_len'] = len(ret_value) log_dict['return'] = _encode_buffer_( ret_value) if self._log_buffers else '' else: # FAILURE log_dict.update({ 'return': None, 'return_exception': ret_value[0], 'return_errno': ret_value[1], 'return_errorcode': errno.errorcode[ret_value[1]] }) if not self._lib_mode: if not self._log_filter.match(log_dict): return if self._log_json and not self._lib_mode: self._logger.info(json.dumps(log_dict, sort_keys=True)[1:-1]) return elif self._lib_mode: log_dict['time'] = time.time_ns() send(log_dict) return log_out = ' '.join([ '%s %s' % (func.__name__, format_pattern.format(**log_dict)), '{%s}' % STATUS_DICT[ret_status], '[ pid = %d %suid = %d ]' % (pid, ('%s ' % p_cmdname) if len(p_cmdname) > 0 else '', uid), '( r = %s )' % str(log_dict['return']) if log_dict['status'] else '( %s = %d )' % ret_value ]) self._logger.info(log_out)