- docs/did_you_know/: one short tip per feature added since May 2026 (<date>-<slug>.md, YAML front matter: added/tags/details), listed newest first; index.md describes the format for adding more. - New pages for features that had none: launching, command_history, tab_completion, live_gui, tweak, scanning, status_server, channel_access, shared_files, schematic_panels. Wired into index.rst together with the existing queues page. - Fix archiver_stripchart/listening_monitor: the method is archiver.strip_plot (strip_chart does not exist on DataHub); document the per-device .strip_plot(). - NAMESPACE_MONITOR_FORMAT.md: scan monitoring is wired into Daq's scan callbacks since 2026-09-09. - scripts/eco-docs (+ -install-user-service): build the docs from an own clone and serve them over HTTP as user systemd units, rebuilt by a timer; a failed build never replaces the served site.
68 lines
2.8 KiB
Markdown
68 lines
2.8 KiB
Markdown
# Shared files: group permissions on results and configuration
|
|
|
|
Several accounts write to the same trees: the shared `gac-bernina` account and
|
|
personal ones. That applies to the results under `/sf/<instrument>/data/<pgroup>/res`
|
|
(run data, run tables, scan info, presets, ...) and to the shared configuration
|
|
under `/sf/<instrument>/code/<account>/`. Everyone in the group must be able to
|
|
add to and rewrite these, which needs directories at `2775` (group-writable
|
|
plus setgid) and files at `664`. Python's defaults (`mkdir` → `755`,
|
|
`open(..., "w")` → `644`) silently produce files nobody else can change.
|
|
Typically you only notice when the next person can't store a run.
|
|
|
|
eco now creates everything it writes with the right mode and group, and warns
|
|
(without failing) when it meets something wrong. The warning names the
|
|
**account that owns** the offending path, since only that account (or root)
|
|
can fix it, and gives the `chgrp`/`chmod` command.
|
|
|
|
## Finding out what is wrong
|
|
|
|
```python
|
|
from eco.utilities import datafiles
|
|
|
|
datafiles.check_group_writable("/sf/bernina/data/p23415/res/run_data")
|
|
```
|
|
|
|
This lists every level that is not group-writable or has the wrong group,
|
|
together with its owner. It changes nothing.
|
|
|
|
## Repairing a tree
|
|
|
|
Run this **as the account that owns the tree**. A personal account cannot
|
|
repair `gac-bernina`'s directories.
|
|
|
|
```python
|
|
datafiles.repair_tree("/sf/bernina/code/gac-bernina/eco_cnf_bernina/memory")
|
|
```
|
|
|
|
It fixes everything this account can fix below the path, and lists the owner
|
|
of everything it cannot.
|
|
|
|
`replace_unowned=True` goes further: it replaces entries owned by *another*
|
|
account with identical copies owned by this one. That changes ownership and
|
|
cannot be undone without root, so try `dry_run=True` first. `dry_run` only
|
|
affects that ownership step; the ordinary chmod/chgrp fixes are still applied.
|
|
|
|
## Which group?
|
|
|
|
Inside a pgroup tree, the pgroup. Elsewhere, the group of the enclosing
|
|
directory (what setgid would have given). Only groups this account belongs to
|
|
can be set.
|
|
|
|
## For code that writes files
|
|
|
|
Use these instead of the plain calls, rather than adding a `chmod` by hand:
|
|
|
|
| Instead of | Use |
|
|
|------------|-----|
|
|
| `Path(p).mkdir(parents=True)` | `datafiles.ensure_dir(p)`: applies `2775` to **every** level it creates |
|
|
| `open(p, "w")` | `datafiles.open_group_writable(p, "w")` |
|
|
| a file written by another library (`to_pickle`, `shutil.copyfile`, ...) | `datafiles.ensure_group_writable(p)` afterwards |
|
|
|
|
Never `chmod(0o775)` a directory: that clears the setgid bit, after which new
|
|
files get the creator's primary group instead of the pgroup.
|
|
|
|
If an existing file is not writable by this session but its directory is,
|
|
`open_group_writable` replaces the file (write to a temporary file, then
|
|
rename) instead of failing, so a second account can still store a value. This
|
|
is reported once, since it changes the file's owner.
|