Skip to content

Latest commit

 

History

History
284 lines (239 loc) · 14.9 KB

MIGRATION.md

File metadata and controls

284 lines (239 loc) · 14.9 KB

Migrating from CCFE 1.x to 2.0

v2.0 reorganises where files live so that each kind of thing has one obvious home. The .menu / .form / .item file formats are unchanged — only the directories move. A fresh install.sh (or a package) lays everything out correctly; this note is for upgrading an existing 1.x install and for anyone who keeps personal menus.

What moved

Thing 1.x location 2.0 location
Perl program <prefix>/bin/ccfe unchanged
Perl modules <prefix>/lib/perl5/CCFE/ unchanged
System menus & forms <prefix>/lib/ccfe/ <prefix>/share/ccfe/objects/ccfe/
Themes (doc samples) <prefix>/share/ccfe/themes/
System config <prefix>/etc/ccfe.conf unchanged
Per-user menus ~/.ccfe/<name>/ ~/.local/share/ccfe/<name>/ (XDG)
Per-user config ~/.ccfe/<name>.conf ~/.config/ccfe/<name>.conf (XDG)

~/.ccfe/ is still searched as a fallback, so existing personal menus keep working until you move them. XDG_DATA_HOME / XDG_CONFIG_HOME are honoured if set.

Steps

  1. Reinstall over the same prefix (system menus/forms/themes land in the new share/ccfe/ locations):

    cd src && sh install.sh -b -p "<prefix>"
  2. Move your personal menus and config to the XDG dirs (optional — the ~/.ccfe/ fallback keeps them working meanwhile):

    mkdir -p ~/.local/share/ccfe ~/.config/ccfe
    mv ~/.ccfe/ccfe        ~/.local/share/ccfe/      # your menu tree(s)
    mv ~/.ccfe/*.conf      ~/.config/ccfe/           # your per-user config
  3. Plugins: packaged plugins that discover the menu directory should read OBJ_DIR from ccfe -c (it falls back to the old LIB_DIR name for 1.x). The bundled ccfe-plugin-sysmon/install.sh shows the pattern.

Compatibility notes

  • ccfe -c now prints OBJ_DIR (menus/forms) and THEME_DIR in addition to LIB_DIR. LIB_DIR is retained for older plugin scripts.
  • The CCFE_OBJ_DIR environment variable overrides the objects directory; the old CCFE_LIB_DIR is no longer read (ccfe still sets it to <prefix>/lib for plugins written for 1.x).
  • Paths are now resolved at runtime from the program's own location, so the installed ccfe is byte-identical to the source and the whole install is relocatable — move the prefix and it still works. A split (FHS-style) layout is selected with environment variables: CCFE_PREFIX, CCFE_ETC_DIR, CCFE_MSG_DIR, CCFE_LOG_DIR, CCFE_OBJ_DIR, CCFE_THEME_DIR (e.g. CCFE_ETC_DIR=/etc/ccfe).
  • Nothing about the .menu / .form / .item / .conf syntax changed.

Migrating from CCFE 2.4 to 3.0

Substitution is shell-safe

%{ID} and %{ARGVn} now stand for one safely quoted shell word: the value in single quotes, an empty value as ''. A field value can no longer add arguments or commands. %r{ID} is the raw text, as before. $NAME config variables are expanded in the template before values are substituted, so a value that reads $NAME reaches the command as typed. A list_cmd command is run by the shell, with %{ID} quoted the same way.

An old item breaks in one of three ways. ccfe -k NAME reports the first two with file and line, and the log gets the same warning when the form loads.

  • A placeholder already in quotes ('%{X}', "%{X}", "...%{X}...") is quoted twice. Remove the quotes and write %{X}. For a shell test, [ "x%{X}" = xYES ] becomes [ %{X} = YES ].
  • An option that ends in a quote character (option = -c") was the old way to quote the value. Remove the quote: option = -c. The quote is ignored.
  • One field meant to supply several words (a value -a -b for the command) now arrives as one word. Use %r{ID} where the words are meant to split, or make the field a multi-value pick-list (list_cmd = command:multi-val:...), which gives one quoted word per value.

A field that may be empty and must then add no argument needs an option (an empty value adds nothing) or ${CCFE_FIELD_ID:+"$CCFE_FIELD_ID"}; a bare %{ID} gives an empty argument ''. A NUMERIC or INTEGER field can use %r{ID} safely.

Pick-list commands and exit status

  • A command: or json(...) pick-list whose command exits non-zero is now an error: the pop-up shows its standard error (if any) and its exit status, and no entries are listed from that run. Before, only output on standard error made a list fail, so a command such as grep pattern file that matched nothing gave an empty list. Add || true (grep pattern file || true) where an empty result is normal.
  • A default = command: that exits non-zero leaves the field as ERROR!, whatever it printed; a command that exits 0 is used even if it also wrote to standard error.

Restricted mode

  • A bare name in restricted_allow (for example less) now matches only a program found in a system directory of the restricted PATH (owned by root, not world-writable). It no longer matches by the basename of whatever path was typed: ./less and /tmp/x/less are refused. To allow a program in another place, list its absolute path (restricted_allow = /opt/tools/bin/x); that exact path is then accepted.
  • The restricted PATH no longer contains $HOME/bin, . or relative entries.
  • system: / exec: under restricted mode never fall back to a shell: an empty or unparsable command, or one that cannot be executed, ends with status 127.
  • Restricted children get LESSSECURE=1; SHELL, EDITOR, VISUAL and PAGER are set to /bin/false, /bin/false, /bin/false and cat.
  • Restricted mode (restricted = yes, or ccfe -R) now reads only trusted configuration files and menu/form object directories: owned by root (or by the owner of the installed program) and not writable by group or others, with every parent directory the same. Before, any file or directory the user could not currently write was accepted, which the user could arrange with chmod. The per-user locations (~/.config/ccfe, ~/.local/share/ccfe, ~/.ccfe) are no longer read at all under restricted mode. A restricted deployment must keep its config and objects in root-owned, non-group/world- writable directories.
  • -R now stays on even if a trusted config file says restricted = no.
  • Under restricted mode the directory overrides CCFE_PREFIX, CCFE_ETC_DIR, CCFE_BIN_DIR, CCFE_LOG_DIR, CCFE_MSG_DIR, CCFE_OBJ_DIR, CCFE_THEME_DIR and CCFE_KEYMAP_DIR are ignored. If the configuration (not -R) turns restricted mode on while any is set, ccfe restarts itself as ccfe -R with them removed.
  • A configuration reload applies the same trust rules as start-up.
  • run: items and command: pick-lists (list_cmd) under restricted mode are executed as argv from the allowlist, never through a shell. An item that uses shell syntax (;, |, &&, redirection, $(...), backticks) or names a program that is not in restricted_allow is now refused (the browser shows the restricted message, status 127). Rewrite such items as a simple command or as a call to an allowlisted wrapper script. Their child PATH is the restricted PATH: it no longer ends in ..
  • New option argv for run:: run(argv):PROG ARGS starts the program without a shell, with the output browser as usual. Use it when field values are part of the command. Plain run: outside restricted mode still goes through the shell.
  • A form's init { } block and a field's default = command: are run the same way under restricted mode: as a program from the allowlist, never through a shell. One that uses shell syntax or names a program that is not in restricted_allow is refused: for init, the restricted message is shown and the form is not posted; for a default, the field is left as ERROR!. Move such logic into an allowlisted wrapper script.

Instances and configuration

  • theme = NAME in an instance's configuration now falls back to the shipped ccfe.conf.NAME when there is no <instance>.conf.NAME. The theme switcher lists the union. An instance that copied a shipped theme's settings inline can name the theme instead.
  • load_user_objects = no now keeps the per-user object directories (~/.local/share/ccfe/NAME, ~/.ccfe/NAME) out of the search path; before it did nothing. The default stays yes. A no in a system configuration file cannot be undone by a user configuration; a reload applies the same rule. A configuration that said load_user_objects = yes in a user config no longer reads that file a second time (it used to loop).
  • The Shell key (F7 by default) leaves the menu, form and output key bars when the shell escape cannot work: user_shell = DISABLED, a user_shell that is not listed in /etc/shells, or restricted mode. A user_shell path is matched literally against /etc/shells, and an unreadable /etc/shells now means "not a valid shell" instead of an error.

Output filter

  • A menu item may carry filter = json (or a command line) and a form a filter { json } block: the captured stdout of its run: action is filtered when the command ends. json is built in; the browser's v key switches to the raw output. Existing menus and forms are unaffected.

Saving output

  • A save from the output browser now creates the file with mode 0600 (before: 0644; a saved script 0700, before 0755), never writes through a symlink, and asks before replacing an existing file.
  • Under restricted mode the save takes a plain file name and writes it to $XDG_DATA_HOME/ccfe/saved (default ~/.local/share/ccfe/saved); a path is refused. Before, any path the user could write was accepted.

Message files

  • Message files (msg/<lang>/ccfe) are read as data: each line is ID = "text", with \\, \", \n and \t as the only escapes. They are no longer evaluated as Perl, so $ and @ in a message are plain characters and a line carrying anything after the closing quote (other than a comment) is rejected. The shipped file is unchanged in meaning.

Logging

  • The log moved from <prefix>/log/<user>.log to a private per-user file, $XDG_STATE_HOME/ccfe/ccfe.log (default ~/.local/state/ccfe/ccfe.log). The directory is created mode 0700 and the file mode 0600. The installer no longer creates a shared world-writable log directory.
  • A new log_fname setting in global { } names another log file; it wins over the default. CCFE_LOG_DIR still selects a directory holding <user>.log.
  • The log is opened without following a symbolic link. When it cannot be opened, ccfe shows a notice at start-up instead of logging nothing in silence.
  • The value of a hidden field is written as **** wherever field values are logged, including the command line logged after substitution.
  • permit_debug defaults to NO under restricted mode (YES otherwise); a trusted configuration file can still set it.
  • A menu item with run(log): now writes the same record a form does (date, menu and command). Before, a menu item's log option wrote nothing.

Menus, shortcuts and pick-lists

  • exec: now gets the same PATH as system: and run: (the system directories, then the path setting of the configuration), instead of the PATH ccfe was started with. A program that exec: found only because of the caller's PATH must be given its directory in path or be named in full.
  • ccfe -s marks a shortcut found under the legacy ~/.ccfe with ~, as it already did for ~/.local/share/ccfe.
  • A pick-list command that fails, or answers invalid JSON, shows its error and nothing more; the "Nothing to choose from yet" notice is kept for a list that is really empty.
  • The Tab key on a field whose list_cmd is a json(...) source (or any command: source) does nothing, without a beep. Tab cycles through values only for const: lists, BOOLEAN and NULLBOOLEAN fields.

ccfe-build

  • Menu, form and item names are checked on every verb, so ../ names are refused.
  • Values reach the file as typed: a backslash sequence such as \n stays two characters, and a value holding a real line break is refused.
  • Every change is built in a staging directory, checked with ccfe -k, and only then moved into place; a change that does not validate leaves the original untouched. The user config is written mode 0600.
  • Under restricted mode (CCFE_RESTRICTED=1, set by ccfe -R) every verb that writes is refused.
  • install-instance NAME --from DIR installs a menu tree kept elsewhere as an instance, uninstall-instance NAME takes it away, and check-trusted PATH applies the restricted-mode trust rule to a path (see Running a menu tree as its own instance in the README).

Field types and generated forms

  • A new field type INTEGER takes an optional leading - and digits and nothing else (NUMERIC also takes +, , and .). ccfe -k and the form builder know it.
  • ccfe-gen writes a schema integer as an INTEGER field (it was NUMERIC); a number stays NUMERIC. A minimum or maximum is stated under the field; the field does not check it.
  • ccfe-gen writes a repeated argument (an array) whose item schema allows a blank in a value as a comma-separated field (list_sep = ","), so one value can hold a blank; it was always blank-separated. An item schema whose enum or anchored pattern rules a blank out stays blank-separated. The field help says which.

ccfe-login

  • ccfe-login -h (or --help) prints the usage, including that the hook is not a security boundary.
  • ccfe-login status and install-user warn when a profile bash reads before the hooked one exists without the hook (for example a later-created ~/.bash_profile hides a hooked ~/.profile).

List values

  • In a list line (the output of a list command, or a const: list entry), \\ in the value now stands for one backslash, as \ stands for a blank, so a value can end in a backslash. A backslash before any other character is unchanged. A json(...) list value is written this way, so any value (trailing backslash, blanks, tabs) comes back unchanged. A hand-written list that holds two backslashes in a row in a value needs four.

Checking menus and forms

ccfe -k NAME now exits 1 for an item or form with no action, an item with no descr, an empty title, an unknown action verb or verb option (rnu:, run(cnofirm):), a verb that needs an argument and has none, and a list field whose list_cmd is not command:, const: or json(...):. Each is an ERROR line with the file and line. An old menu written as item { id=A descr=B action=run:ls } on one line parsed as an item whose id was the whole line; it fails the check now, and each attribute goes on its own line. -W (--strict) makes the quoting warnings fail the check too; use it in CI.

An instance (ccfe linked under another name) opens NAME.menu or NAME.form when its object path has one, and takes the default messages when it has no msg/<lang>/NAME file.