Open, Inspect and Close SCSI Streams with sg_stream_ctl

sg_stream_ctl lists open SCSI stream IDs, opens a new one, and closes the exact stream you opened, using the ID the device assigns. The examples match the installed sg3-utils 1.46-3ubuntu4 package, whose utility reports version 1.09 from 24 July 2020. The SCSI device must support the stream commands; this is not a generic file or filesystem feature.

Allow about fifteen minutes for an inspection, or longer if you need to test a real stream-writing workflow. You need sg_stream_ctl, access to the target SCSI device, and a maintenance window if the target is shared. These commands send SCSI pass-through requests. They do not write ordinary user data, but opening and closing a stream changes device-side stream state.

1. Confirm the installed command

Check the binary and package before copying an example. These are ordinary, read-only commands:

$ command -v sg_stream_ctl
/usr/bin/sg_stream_ctl
$ sg_stream_ctl --version
version: 1.09 20200724
$ dpkg-query -W -f='${Package} ${Version}\n' sg3-utils
sg3-utils 1.46-3ubuntu4

The output belongs to this installed build. A newer package may add or alter diagnostics, so repeat the version check when moving the procedure to another host. The device argument is mandatory and normally looks like /dev/sg3; use the path that identifies your target, not the placeholder below.

Checkpoint: set a shell variable after checking the path. Reading the device mapping is host-specific, so do not guess an ID:

$ DEVICE=/dev/sgX
$ test -e "$DEVICE" && echo "device exists"
device exists

If access is denied, stop and fix device permissions or the service account's group membership through your normal system administration process. Do not solve an uncertain device selection with sudo.

2. List the currently open streams

GET STREAM STATUS is the default action, so the following two commands are equivalent. The first spells out the action and is easier to recognise in a script:

$ sg_stream_ctl --get "$DEVICE"
$ sg_stream_ctl "$DEVICE"

Without --brief, the program prints decoded fields from the response. The exact wording depends on the response returned by the device. For automation, request one decimal stream ID per output line:

$ sg_stream_ctl --get --brief "$DEVICE"
<one decimal stream ID per line, if any are open>

Those numbers are the currently open stream IDs. An empty successful response means that no IDs were returned in the allocated response. With --brief, an error is reported as -1 on standard output and related diagnostics go to standard error, so also check the command's exit status in scripts:

$ if sg_stream_ctl --get --brief "$DEVICE"; then
>     echo "status request completed"
> else
>     echo "status request failed" >&2
> fi

The default GET STREAM STATUS allocation length is 248 bytes. If you expect many open IDs, increase it with --maxlen, subject to the device and command limits:

$ sg_stream_ctl --get --brief --maxlen=1024 "$DEVICE"

--id=SID changes the query to start at that stream ID, inclusive. It filters out lower IDs; it does not select a stream for later writing.

3. Open a stream and capture its assigned ID

Opening a stream is a state-changing device operation. Before doing it, make sure the software that will issue WRITE STREAM commands is ready to use the returned ID. Do not invent an ID, and do not reuse one from an old run. The device assigns the ID.

Use --brief --open when you want a clean value for a shell variable:

$ if STREAM_ID=$(sg_stream_ctl --brief --open "$DEVICE"); then
>     printf 'assigned stream ID: %s\n' "$STREAM_ID"
> else
>     printf 'open failed; reported value: %s\n' "$STREAM_ID" >&2
> fi
assigned stream ID: <assigned stream ID from the device>

A successful brief open prints one number between 1 and 65535. The --id option is ignored for an open request. With normal output, the response includes the assigned stream ID in words rather than only the number. Keep the captured value with the process that will write the stream.

Checkpoint: verify that the ID now appears in a status query before sending any WRITE STREAM command:

$ sg_stream_ctl --get --brief --id="$STREAM_ID" "$DEVICE"
<the captured ID, if the stream is still open>

If the open fails, preserve the diagnostic and inspect the device's SCSI sense data through your usual storage troubleshooting process. Do not retry repeatedly against a busy production device.

4. Close the exact stream

Closing removes the resources associated with the stream ID. The data already written to its logical block addresses remains, but the stream's device-side resources do not. Treat close as an operational boundary, especially if another process may still be writing.

After the WRITE STREAM work has finished, pass the captured ID to --close:

$ sg_stream_ctl --close --id="$STREAM_ID" "$DEVICE"
<device-specific success response, if printed>

The exact prose can vary. A zero exit status is the useful success signal. --close selects STREAM CONTROL with the close value, and its default ID is 0, which is invalid. Always provide the actual ID instead of relying on the default.

Verify that the ID is no longer reported:

$ sg_stream_ctl --get --brief --id="$STREAM_ID" "$DEVICE"
$ printf 'status: %s\n' "$?"
status: 0

An empty output is the expected result when no stream at or above that starting ID is open. If another stream has a larger ID, it may still be listed. If the close request fails, do not assume that resources were released; rerun the status query and investigate before continuing.

5. Keep the option boundaries clear

--open and --close select STREAM CONTROL. --ctl=1 is equivalent to open and --ctl=2 is equivalent to close. Values 0 and 3 are reserved, so leave --ctl out unless you have a specific reason to express the command value explicitly.

--maxlen defaults to 8 bytes for STREAM CONTROL and 248 bytes for GET STREAM STATUS. That is why an open does not need the larger status allocation, while a status query may need one. --readonly asks the operating system to open the device read-only, but the manpage warns that pass-through operations are difficult for an operating system to classify and may still require read-write device permissions. It is not a guarantee that a command is harmless.

Use --verbose only when you need debug output. Keep diagnostics separate from the brief numeric output if a script consumes standard output. The command exits with status 0 on success; otherwise consult the sg3-utils error and sense information rather than treating a printed number alone as proof of success.

Done means