@@ -27,7 +27,10 @@ import (
2727type ProcBase int
2828
2929const (
30- unimplementedProcBaseRoot ProcBase = iota
30+ // Use /proc. Note that this mode may be more expensive because we have to
31+ // take steps to try to avoid leaking unmasked procfs handles, so you
32+ // should use [ProcBaseSelf] if you can.
33+ ProcBaseRoot ProcBase = iota
3134 // Use /proc/self. For most programs, this is the standard choice.
3235 ProcBaseSelf
3336 // Use /proc/thread-self. In multi-threaded programs where one thread has
@@ -38,6 +41,8 @@ const (
3841
3942func (b ProcBase ) toPathrsBase () (pathrsProcBase , error ) {
4043 switch b {
44+ case ProcBaseRoot :
45+ return pathrsProcRoot , nil
4146 case ProcBaseSelf :
4247 return pathrsProcSelf , nil
4348 case ProcBaseThreadSelf :
@@ -47,36 +52,65 @@ func (b ProcBase) toPathrsBase() (pathrsProcBase, error) {
4752 }
4853}
4954
55+ func (b ProcBase ) namePrefix () string {
56+ switch b {
57+ case ProcBaseRoot :
58+ return "/proc/"
59+ case ProcBaseSelf :
60+ return "/proc/self/"
61+ case ProcBaseThreadSelf :
62+ return "/proc/thread-self/"
63+ default :
64+ return "<invalid procfs base>/"
65+ }
66+ }
67+
5068// ProcHandleCloser is a callback that needs to be called when you are done
51- // operating on an *os.File fetched using ProcThreadSelfOpen.
69+ // operating on an *os.File fetched using [ ProcThreadSelfOpen] .
5270type ProcHandleCloser func ()
5371
54- // TODO: Consider exporting procOpen once we have ProcBaseRoot.
55-
72+ // TODO: Should we expose procOpen?
5673func procOpen (base ProcBase , path string , flags int ) (* os.File , ProcHandleCloser , error ) {
5774 pathrsBase , err := base .toPathrsBase ()
5875 if err != nil {
5976 return nil , nil , err
6077 }
78+ namePrefix := base .namePrefix ()
79+
6180 switch base {
62- case ProcBaseSelf :
81+ case ProcBaseRoot , ProcBaseSelf :
6382 fd , err := pathrsProcOpen (pathrsBase , path , flags )
6483 if err != nil {
6584 return nil , nil , err
6685 }
67- return os .NewFile (fd , "/proc/self/" + path ), nil , nil
86+ return os .NewFile (fd , namePrefix + path ), nil , nil
6887 case ProcBaseThreadSelf :
6988 runtime .LockOSThread ()
7089 fd , err := pathrsProcOpen (pathrsBase , path , flags )
7190 if err != nil {
7291 runtime .UnlockOSThread ()
7392 return nil , nil , err
7493 }
75- return os .NewFile (fd , "/proc/thread-self/" + path ), runtime .UnlockOSThread , nil
94+ return os .NewFile (fd , namePrefix + path ), runtime .UnlockOSThread , nil
7695 }
7796 panic ("unreachable" )
7897}
7998
99+ // ProcRootOpen safely opens a given path from inside /proc/.
100+ //
101+ // This function must only be used for accessing global information from procfs
102+ // (such as /proc/cpuinfo) or information about other processes (such as
103+ // /proc/1). Accessing your own process information should be done using
104+ // [ProcSelfOpen] or [ProcThreadSelfOpen].
105+ func ProcRootOpen (path string , flags int ) (* os.File , error ) {
106+ file , closer , err := procOpen (ProcBaseRoot , path , flags )
107+ if closer != nil {
108+ // should not happen
109+ panic ("non-zero closer returned from procOpen(ProcBaseRoot)" )
110+ }
111+ return file , err
112+ }
113+
80114// ProcSelfOpen safely opens a given path from inside /proc/self/.
81115//
82116// This method is recommend for getting process information about the current
@@ -86,13 +120,14 @@ func procOpen(base ProcBase, path string, flags int) (*os.File, ProcHandleCloser
86120//
87121// For such non-heterogeneous processes, /proc/self may reference to a task
88122// that has different state from the current goroutine and so it may be
89- // preferable to use ProcThreadSelfOpen. The same is true if a user really
123+ // preferable to use [ ProcThreadSelfOpen] . The same is true if a user really
90124// wants to inspect the current OS thread's information (such as
91125// /proc/thread-self/stack or /proc/thread-self/status which is always uniquely
92126// per-thread).
93127//
94- // Unlike ProcThreadSelfOpen, this method does not involve locking the
95- // goroutine to the current OS thread and so is simpler to use.
128+ // Unlike [ProcThreadSelfOpen], this method does not involve locking the
129+ // goroutine to the current OS thread and so is simpler to use and
130+ // theoretically has slightly less overhead.
96131func ProcSelfOpen (path string , flags int ) (* os.File , error ) {
97132 file , closer , err := procOpen (ProcBaseSelf , path , flags )
98133 if closer != nil {
@@ -105,7 +140,7 @@ func ProcSelfOpen(path string, flags int) (*os.File, error) {
105140// ProcThreadSelfOpen safely opens a given path from inside /proc/thread-self/.
106141//
107142// Most Go processes have heterogeneous threads (all threads have most of the
108- // same kernel state such as CLONE_FS) and so ProcSelfOpen is preferable for
143+ // same kernel state such as CLONE_FS) and so [ ProcSelfOpen] is preferable for
109144// most users.
110145//
111146// For non-heterogeneous threads, or users that actually want thread-specific
@@ -129,7 +164,7 @@ func ProcThreadSelfOpen(path string, flags int) (*os.File, ProcHandleCloser, err
129164//
130165// This is effectively equivalent to doing a Proc*Open(O_PATH|O_NOFOLLOW) of
131166// the path and then doing unix.Readlinkat(fd, ""), but with the benefit that
132- // thread locking is not necessary for ProcBaseThreadSelf.
167+ // thread locking is not necessary for [ ProcBaseThreadSelf] .
133168func ProcReadlink (base ProcBase , path string ) (string , error ) {
134169 pathrsBase , err := base .toPathrsBase ()
135170 if err != nil {
0 commit comments