gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/website/cmd/syscalldocs/main.go (about)

     1  // Copyright 2019 The gVisor Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Binary syscalldocs generates system call markdown.
    16  package main
    17  
    18  import (
    19  	"bufio"
    20  	"encoding/json"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"path/filepath"
    26  	"sort"
    27  	"strings"
    28  	"text/template"
    29  )
    30  
    31  // CompatibilityInfo is the collection of all information.
    32  type CompatibilityInfo map[string]map[string]ArchInfo
    33  
    34  // ArchInfo is compatbility doc for an architecture.
    35  type ArchInfo struct {
    36  	// Syscalls maps syscall number for the architecture to the doc.
    37  	Syscalls map[uintptr]SyscallDoc `json:"syscalls"`
    38  }
    39  
    40  // SyscallDoc represents a single item of syscall documentation.
    41  type SyscallDoc struct {
    42  	Name    string   `json:"name"`
    43  	Support string   `json:"support"`
    44  	Note    string   `json:"note,omitempty"`
    45  	URLs    []string `json:"urls,omitempty"`
    46  }
    47  
    48  var mdTemplate = template.Must(template.New("out").Parse(`---
    49  title: {{.Title}}
    50  description: Syscall Compatibility Reference Documentation for {{.OS}}/{{.Arch}}
    51  layout: docs
    52  category: Compatibility
    53  weight: 50
    54  permalink: /docs/user_guide/compatibility/{{.OS}}/{{.Arch}}/
    55  include_in_menu: True
    56  ---
    57  
    58  This table is a reference of {{.OS}} syscalls for the {{.Arch}} architecture and
    59  their compatibility status in gVisor. gVisor does not support all syscalls and
    60  some syscalls may have a partial implementation.
    61  
    62  This page is automatically generated from the source code.
    63  
    64  Of {{.Total}} syscalls, {{.Supported}} syscalls have a full or partial
    65  implementation. There are currently {{.Unsupported}} unsupported
    66  syscalls. {{if .Undocumented}}{{.Undocumented}} syscalls are not yet documented.{{end}}
    67  
    68  <table>
    69    <thead>
    70      <tr>
    71        <th>#</th>
    72        <th>Name</th>
    73        <th>Support</th>
    74        <th>Notes</th>
    75      </tr>
    76    </thead>
    77    <tbody>
    78   	{{range $i, $syscall := .Syscalls}}
    79      <tr id="{{.Name}}">
    80        <td><a href="#{{.Name}}">{{.Number}}</a></td>
    81        <td><a href="{{.DocURL}}" target="_blank" rel="noopener">{{.Name}}</a></td>
    82        <td>{{.Support}}</td>
    83  	  <td>{{.Note}} {{range $i, $url := .URLs}}<br/>See: <a href="{{.}}">{{.}}</a>{{end}}</td>
    84      </tr>
    85  	{{end}}
    86    </tbody>
    87  </table>
    88  `))
    89  
    90  // Fatalf writes a message to stderr and exits with error code 1
    91  func Fatalf(format string, a ...any) {
    92  	fmt.Fprintf(os.Stderr, format, a...)
    93  	os.Exit(1)
    94  }
    95  
    96  // syscallDocURL returns a doc url for a given syscall, doing its best to return a url that exists.
    97  func syscallDocURL(name string) string {
    98  	customDocs := map[string]string{
    99  		"io_pgetevents":     "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   100  		"rseq":              "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   101  		"io_uring_setup":    "https://manpages.debian.org/buster-backports/liburing-dev/io_uring_setup.2.en.html",
   102  		"io_uring_enter":    "https://manpages.debian.org/buster-backports/liburing-dev/io_uring_enter.2.en.html",
   103  		"io_uring_register": "https://manpages.debian.org/buster-backports/liburing-dev/io_uring_register.2.en.html",
   104  		"open_tree":         "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   105  		"move_mount":        "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   106  		"fsopen":            "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   107  		"fsconfig":          "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   108  		"fsmount":           "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   109  		"fspick":            "https://man7.org/linux/man-pages/man2/syscalls.2.html",
   110  	}
   111  	if url, ok := customDocs[name]; ok {
   112  		return url
   113  	}
   114  	return fmt.Sprintf("http://man7.org/linux/man-pages/man2/%s.2.html", name)
   115  }
   116  
   117  func main() {
   118  	inputFlag := flag.String("in", "-", "File to input ('-' for stdin)")
   119  	outputDir := flag.String("out", ".", "Directory to output files.")
   120  
   121  	flag.Parse()
   122  
   123  	var input io.Reader
   124  	if *inputFlag == "-" {
   125  		input = os.Stdin
   126  	} else {
   127  		i, err := os.Open(*inputFlag)
   128  		if err != nil {
   129  			Fatalf("Error opening %q: %v", *inputFlag, err)
   130  		}
   131  		input = i
   132  	}
   133  	input = bufio.NewReader(input)
   134  
   135  	var info CompatibilityInfo
   136  	d := json.NewDecoder(input)
   137  	if err := d.Decode(&info); err != nil {
   138  		Fatalf("Error reading json: %v", err)
   139  	}
   140  
   141  	weight := 0
   142  	for osName, osInfo := range info {
   143  		for archName, archInfo := range osInfo {
   144  			outDir := filepath.Join(*outputDir, osName)
   145  			outFile := filepath.Join(outDir, archName+".md")
   146  
   147  			if err := os.MkdirAll(outDir, 0755); err != nil {
   148  				Fatalf("Error creating directory %q: %v", *outputDir, err)
   149  			}
   150  
   151  			f, err := os.OpenFile(outFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
   152  			if err != nil {
   153  				Fatalf("Error opening file %q: %v", outFile, err)
   154  			}
   155  			defer f.Close()
   156  
   157  			weight += 10
   158  			data := struct {
   159  				Title        string
   160  				OS           string
   161  				Arch         string
   162  				Weight       int
   163  				Total        int
   164  				Supported    int
   165  				Unsupported  int
   166  				Undocumented int
   167  				Syscalls     []struct {
   168  					Name    string
   169  					Number  uintptr
   170  					DocURL  string
   171  					Support string
   172  					Note    string
   173  					URLs    []string
   174  				}
   175  			}{
   176  				Title:        strings.Title(osName) + "/" + archName,
   177  				OS:           osName,
   178  				Arch:         archName,
   179  				Weight:       weight,
   180  				Total:        0,
   181  				Supported:    0,
   182  				Unsupported:  0,
   183  				Undocumented: 0,
   184  				Syscalls: []struct {
   185  					Name    string
   186  					Number  uintptr
   187  					DocURL  string
   188  					Support string
   189  					Note    string
   190  					URLs    []string
   191  				}{},
   192  			}
   193  
   194  			for num, s := range archInfo.Syscalls {
   195  				switch s.Support {
   196  				case "Full Support", "Partial Support":
   197  					data.Supported++
   198  				case "Unimplemented":
   199  					data.Unsupported++
   200  				default:
   201  					data.Undocumented++
   202  				}
   203  				data.Total++
   204  
   205  				for i := range s.URLs {
   206  					if !strings.HasPrefix(s.URLs[i], "http://") && !strings.HasPrefix(s.URLs[i], "https://") {
   207  						s.URLs[i] = "https://" + s.URLs[i]
   208  					}
   209  				}
   210  
   211  				data.Syscalls = append(data.Syscalls, struct {
   212  					Name    string
   213  					Number  uintptr
   214  					DocURL  string
   215  					Support string
   216  					Note    string
   217  					URLs    []string
   218  				}{
   219  					Name:    s.Name,
   220  					Number:  num,
   221  					DocURL:  syscallDocURL(s.Name),
   222  					Support: s.Support,
   223  					Note:    s.Note,
   224  					URLs:    s.URLs,
   225  				})
   226  			}
   227  
   228  			sort.Slice(data.Syscalls, func(i, j int) bool {
   229  				return data.Syscalls[i].Number < data.Syscalls[j].Number
   230  			})
   231  
   232  			if err := mdTemplate.Execute(f, data); err != nil {
   233  				Fatalf("Error writing file %q: %v", outFile, err)
   234  			}
   235  		}
   236  	}
   237  }