mirror of https://github.com/prometheus/prometheus
Julien Pivotto
4 years ago
61 changed files with 5271 additions and 1121 deletions
@ -0,0 +1,72 @@
|
||||
# HTTPS and authentication |
||||
|
||||
Prometheus supports basic authentication and TLS. |
||||
This is **experimental** and might change in the future. |
||||
|
||||
To specify which web configuration file to load, use the `--web.config.file` flag. |
||||
|
||||
The file is written in [YAML format](https://en.wikipedia.org/wiki/YAML), |
||||
defined by the scheme described below. |
||||
Brackets indicate that a parameter is optional. For non-list parameters the |
||||
value is set to the specified default. |
||||
|
||||
The file is read upon every http request, such as any change in the |
||||
configuration and the certificates is picked up immediately. |
||||
|
||||
Generic placeholders are defined as follows: |
||||
|
||||
* `<boolean>`: a boolean that can take the values `true` or `false` |
||||
* `<filename>`: a valid path in the current working directory |
||||
* `<secret>`: a regular string that is a secret, such as a password |
||||
* `<string>`: a regular string |
||||
|
||||
``` |
||||
tls_server_config: |
||||
# Certificate and key files for server to use to authenticate to client. |
||||
cert_file: <filename> |
||||
key_file: <filename> |
||||
|
||||
# Server policy for client authentication. Maps to ClientAuth Policies. |
||||
# For more detail on clientAuth options: [ClientAuthType](https://golang.org/pkg/crypto/tls/#ClientAuthType) |
||||
[ client_auth_type: <string> | default = "NoClientCert" ] |
||||
|
||||
# CA certificate for client certificate authentication to the server. |
||||
[ client_ca_file: <filename> ] |
||||
|
||||
# Minimum TLS version that is acceptable. |
||||
[ min_version: <string> | default = "TLS12" ] |
||||
|
||||
# Maximum TLS version that is acceptable. |
||||
[ max_version: <string> | default = "TLS13" ] |
||||
|
||||
# List of supported cipher suites for TLS versions up to TLS 1.2. If empty, |
||||
# Go default cipher suites are used. Available cipher suites are documented |
||||
# in the go documentation: |
||||
# https://golang.org/pkg/crypto/tls/#pkg-constants |
||||
[ cipher_suites: |
||||
[ - <string> ] ] |
||||
|
||||
# prefer_server_cipher_suites controls whether the server selects the |
||||
# client's most preferred ciphersuite, or the server's most preferred |
||||
# ciphersuite. If true then the server's preference, as expressed in |
||||
# the order of elements in cipher_suites, is used. |
||||
[ prefer_server_cipher_suites: <bool> | default = true ] |
||||
|
||||
# Elliptic curves that will be used in an ECDHE handshake, in preference |
||||
# order. Available curves are documented in the go documentation: |
||||
# https://golang.org/pkg/crypto/tls/#CurveID |
||||
[ curve_preferences: |
||||
[ - <string> ] ] |
||||
|
||||
http_server_config: |
||||
# Enable HTTP/2 support. Note that HTTP/2 is only supported with TLS. |
||||
# This can not be changed on the fly. |
||||
[ http2: <boolean> | default = true ] |
||||
|
||||
# Usernames and hashed passwords that have full access to the web |
||||
# server via basic authentication. If empty, no basic authentication is |
||||
# required. Passwords are hashed with bcrypt. |
||||
basic_auth_users: |
||||
[ <string>: <secret> ... ] |
||||
``` |
||||
|
@ -0,0 +1,201 @@
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,81 @@
|
||||
# HTTPS Package for Prometheus |
||||
|
||||
The `https` directory contains a Go package and a sample configuration file for |
||||
running `node_exporter` with HTTPS instead of HTTP. We currently support TLS 1.3 |
||||
and TLS 1.2. |
||||
|
||||
To run a server with TLS, use the flag `--web.config`. |
||||
|
||||
e.g. `./node_exporter --web.config="web-config.yml"` |
||||
If the config is kept within the https directory. |
||||
|
||||
The config file should be written in YAML format, and is reloaded on each connection to check for new certificates and/or authentication policy. |
||||
|
||||
## Sample Config |
||||
|
||||
``` |
||||
tls_server_config: |
||||
# Certificate and key files for server to use to authenticate to client. |
||||
cert_file: <filename> |
||||
key_file: <filename> |
||||
|
||||
# Server policy for client authentication. Maps to ClientAuth Policies. |
||||
# For more detail on clientAuth options: [ClientAuthType](https://golang.org/pkg/crypto/tls/#ClientAuthType) |
||||
[ client_auth_type: <string> | default = "NoClientCert" ] |
||||
|
||||
# CA certificate for client certificate authentication to the server. |
||||
[ client_ca_file: <filename> ] |
||||
|
||||
# Minimum TLS version that is acceptable. |
||||
[ min_version: <string> | default = "TLS12" ] |
||||
|
||||
# Maximum TLS version that is acceptable. |
||||
[ max_version: <string> | default = "TLS13" ] |
||||
|
||||
# List of supported cipher suites for TLS versions up to TLS 1.2. If empty, |
||||
# Go default cipher suites are used. Available cipher suites are documented |
||||
# in the go documentation: |
||||
# https://golang.org/pkg/crypto/tls/#pkg-constants |
||||
[ cipher_suites: |
||||
[ - <string> ] ] |
||||
|
||||
# prefer_server_cipher_suites controls whether the server selects the |
||||
# client's most preferred ciphersuite, or the server's most preferred |
||||
# ciphersuite. If true then the server's preference, as expressed in |
||||
# the order of elements in cipher_suites, is used. |
||||
[ prefer_server_cipher_suites: <bool> | default = true ] |
||||
|
||||
# Elliptic curves that will be used in an ECDHE handshake, in preference |
||||
# order. Available curves are documented in the go documentation: |
||||
# https://golang.org/pkg/crypto/tls/#CurveID |
||||
[ curve_preferences: |
||||
[ - <string> ] ] |
||||
|
||||
http_server_config: |
||||
# Enable HTTP/2 support. Note that HTTP/2 is only supported with TLS. |
||||
# This can not be changed on the fly. |
||||
[ http2: <bool> | default = true ] |
||||
|
||||
# Usernames and hashed passwords that have full access to the web |
||||
# server via basic authentication. If empty, no basic authentication is |
||||
# required. Passwords are hashed with bcrypt. |
||||
basic_auth_users: |
||||
[ <string>: <secret> ... ] |
||||
``` |
||||
|
||||
## About bcrypt |
||||
|
||||
There are several tools out there to generate bcrypt passwords, e.g. |
||||
[htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html): |
||||
|
||||
`htpasswd -nBC 10 "" | tr -d ':\n'` |
||||
|
||||
That command will prompt you for a password and output the hashed password, |
||||
which will look something like: |
||||
`$2y$10$X0h1gDsPszWURQaxFh.zoubFi6DXncSjhoQNJgRrnGs7EsimhC7zG` |
||||
|
||||
The cost (10 in the example) influences the time it takes for computing the |
||||
hash. A higher cost will en up slowing down the authentication process. |
||||
Depending on the machine, a cost of 10 will take about ~70ms where a cost of |
||||
18 can take up to a few seconds. That hash will be computed on every |
||||
password-protected request. |
@ -0,0 +1,26 @@
|
||||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package kingpinflag |
||||
|
||||
import ( |
||||
"gopkg.in/alecthomas/kingpin.v2" |
||||
) |
||||
|
||||
// AddFlags adds the flags used by this package to the Kingpin application.
|
||||
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
|
||||
func AddFlags(a *kingpin.Application) *string { |
||||
return a.Flag( |
||||
"web.config.file", |
||||
"[EXPERIMENTAL] Path to configuration file that can enable TLS or authentication.", |
||||
).Default("").String() |
||||
} |
@ -0,0 +1,343 @@
|
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package https allows the implementation of TLS.
|
||||
package https |
||||
|
||||
import ( |
||||
"crypto/tls" |
||||
"crypto/x509" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net" |
||||
"net/http" |
||||
"path/filepath" |
||||
|
||||
"github.com/go-kit/kit/log" |
||||
"github.com/go-kit/kit/log/level" |
||||
"github.com/pkg/errors" |
||||
config_util "github.com/prometheus/common/config" |
||||
"gopkg.in/yaml.v2" |
||||
) |
||||
|
||||
var ( |
||||
errNoTLSConfig = errors.New("TLS config is not present") |
||||
) |
||||
|
||||
type Config struct { |
||||
TLSConfig TLSStruct `yaml:"tls_server_config"` |
||||
HTTPConfig HTTPStruct `yaml:"http_server_config"` |
||||
Users map[string]config_util.Secret `yaml:"basic_auth_users"` |
||||
} |
||||
|
||||
type TLSStruct struct { |
||||
TLSCertPath string `yaml:"cert_file"` |
||||
TLSKeyPath string `yaml:"key_file"` |
||||
ClientAuth string `yaml:"client_auth_type"` |
||||
ClientCAs string `yaml:"client_ca_file"` |
||||
CipherSuites []cipher `yaml:"cipher_suites"` |
||||
CurvePreferences []curve `yaml:"curve_preferences"` |
||||
MinVersion tlsVersion `yaml:"min_version"` |
||||
MaxVersion tlsVersion `yaml:"max_version"` |
||||
PreferServerCipherSuites bool `yaml:"prefer_server_cipher_suites"` |
||||
} |
||||
|
||||
// SetDirectory joins any relative file paths with dir.
|
||||
func (t *TLSStruct) SetDirectory(dir string) { |
||||
t.TLSCertPath = config_util.JoinDir(dir, t.TLSCertPath) |
||||
t.TLSKeyPath = config_util.JoinDir(dir, t.TLSKeyPath) |
||||
t.ClientCAs = config_util.JoinDir(dir, t.ClientCAs) |
||||
} |
||||
|
||||
type HTTPStruct struct { |
||||
HTTP2 bool `yaml:"http2"` |
||||
} |
||||
|
||||
func getConfig(configPath string) (*Config, error) { |
||||
content, err := ioutil.ReadFile(configPath) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
c := &Config{ |
||||
TLSConfig: TLSStruct{ |
||||
MinVersion: tls.VersionTLS12, |
||||
MaxVersion: tls.VersionTLS13, |
||||
PreferServerCipherSuites: true, |
||||
}, |
||||
HTTPConfig: HTTPStruct{HTTP2: true}, |
||||
} |
||||
err = yaml.UnmarshalStrict(content, c) |
||||
c.TLSConfig.SetDirectory(filepath.Dir(configPath)) |
||||
return c, err |
||||
} |
||||
|
||||
func getTLSConfig(configPath string) (*tls.Config, error) { |
||||
c, err := getConfig(configPath) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return ConfigToTLSConfig(&c.TLSConfig) |
||||
} |
||||
|
||||
// ConfigToTLSConfig generates the golang tls.Config from the TLSStruct config.
|
||||
func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) { |
||||
if c.TLSCertPath == "" && c.TLSKeyPath == "" && c.ClientAuth == "" && c.ClientCAs == "" { |
||||
return nil, errNoTLSConfig |
||||
} |
||||
|
||||
if c.TLSCertPath == "" { |
||||
return nil, errors.New("missing cert_file") |
||||
} |
||||
|
||||
if c.TLSKeyPath == "" { |
||||
return nil, errors.New("missing key_file") |
||||
} |
||||
|
||||
loadCert := func() (*tls.Certificate, error) { |
||||
cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath) |
||||
if err != nil { |
||||
return nil, errors.Wrap(err, "failed to load X509KeyPair") |
||||
} |
||||
return &cert, nil |
||||
} |
||||
|
||||
// Confirm that certificate and key paths are valid.
|
||||
if _, err := loadCert(); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
cfg := &tls.Config{ |
||||
MinVersion: (uint16)(c.MinVersion), |
||||
MaxVersion: (uint16)(c.MaxVersion), |
||||
PreferServerCipherSuites: c.PreferServerCipherSuites, |
||||
} |
||||
|
||||
cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { |
||||
return loadCert() |
||||
} |
||||
|
||||
var cf []uint16 |
||||
for _, c := range c.CipherSuites { |
||||
cf = append(cf, (uint16)(c)) |
||||
} |
||||
if len(cf) > 0 { |
||||
cfg.CipherSuites = cf |
||||
} |
||||
|
||||
var cp []tls.CurveID |
||||
for _, c := range c.CurvePreferences { |
||||
cp = append(cp, (tls.CurveID)(c)) |
||||
} |
||||
if len(cp) > 0 { |
||||
cfg.CurvePreferences = cp |
||||
} |
||||
|
||||
if c.ClientCAs != "" { |
||||
clientCAPool := x509.NewCertPool() |
||||
clientCAFile, err := ioutil.ReadFile(c.ClientCAs) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
clientCAPool.AppendCertsFromPEM(clientCAFile) |
||||
cfg.ClientCAs = clientCAPool |
||||
} |
||||
|
||||
switch c.ClientAuth { |
||||
case "RequestClientCert": |
||||
cfg.ClientAuth = tls.RequestClientCert |
||||
case "RequireClientCert": |
||||
cfg.ClientAuth = tls.RequireAnyClientCert |
||||
case "VerifyClientCertIfGiven": |
||||
cfg.ClientAuth = tls.VerifyClientCertIfGiven |
||||
case "RequireAndVerifyClientCert": |
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert |
||||
case "", "NoClientCert": |
||||
cfg.ClientAuth = tls.NoClientCert |
||||
default: |
||||
return nil, errors.New("Invalid ClientAuth: " + c.ClientAuth) |
||||
} |
||||
|
||||
if c.ClientCAs != "" && cfg.ClientAuth == tls.NoClientCert { |
||||
return nil, errors.New("Client CA's have been configured without a Client Auth Policy") |
||||
} |
||||
|
||||
return cfg, nil |
||||
} |
||||
|
||||
// Listen starts the server on the given address. Based on the file
|
||||
// tlsConfigPath, TLS or basic auth could be enabled.
|
||||
func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error { |
||||
listener, err := net.Listen("tcp", server.Addr) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer listener.Close() |
||||
return Serve(listener, server, tlsConfigPath, logger) |
||||
} |
||||
|
||||
// Server starts the server on the given listener. Based on the file
|
||||
// tlsConfigPath, TLS or basic auth could be enabled.
|
||||
func Serve(l net.Listener, server *http.Server, tlsConfigPath string, logger log.Logger) error { |
||||
if tlsConfigPath == "" { |
||||
level.Info(logger).Log("msg", "TLS is disabled.", "http2", false) |
||||
return server.Serve(l) |
||||
} |
||||
|
||||
if err := validateUsers(tlsConfigPath); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Setup basic authentication.
|
||||
var handler http.Handler = http.DefaultServeMux |
||||
if server.Handler != nil { |
||||
handler = server.Handler |
||||
} |
||||
server.Handler = &userAuthRoundtrip{ |
||||
tlsConfigPath: tlsConfigPath, |
||||
logger: logger, |
||||
handler: handler, |
||||
} |
||||
|
||||
c, err := getConfig(tlsConfigPath) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
config, err := ConfigToTLSConfig(&c.TLSConfig) |
||||
switch err { |
||||
case nil: |
||||
if !c.HTTPConfig.HTTP2 { |
||||
server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) |
||||
} |
||||
// Valid TLS config.
|
||||
level.Info(logger).Log("msg", "TLS is enabled.", "http2", c.HTTPConfig.HTTP2) |
||||
case errNoTLSConfig: |
||||
// No TLS config, back to plain HTTP.
|
||||
level.Info(logger).Log("msg", "TLS is disabled.", "http2", false) |
||||
return server.Serve(l) |
||||
default: |
||||
// Invalid TLS config.
|
||||
return err |
||||
} |
||||
|
||||
server.TLSConfig = config |
||||
|
||||
// Set the GetConfigForClient method of the HTTPS server so that the config
|
||||
// and certs are reloaded on new connections.
|
||||
server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { |
||||
return getTLSConfig(tlsConfigPath) |
||||
} |
||||
return server.ServeTLS(l, "", "") |
||||
} |
||||
|
||||
// Validate configuration file by reading the configuration and the certificates.
|
||||
func Validate(tlsConfigPath string) error { |
||||
if tlsConfigPath == "" { |
||||
return nil |
||||
} |
||||
if err := validateUsers(tlsConfigPath); err != nil { |
||||
return err |
||||
} |
||||
c, err := getConfig(tlsConfigPath) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
_, err = ConfigToTLSConfig(&c.TLSConfig) |
||||
if err == errNoTLSConfig { |
||||
return nil |
||||
} |
||||
return err |
||||
} |
||||
|
||||
type cipher uint16 |
||||
|
||||
func (c *cipher) UnmarshalYAML(unmarshal func(interface{}) error) error { |
||||
var s string |
||||
err := unmarshal((*string)(&s)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
for _, cs := range tls.CipherSuites() { |
||||
if cs.Name == s { |
||||
*c = (cipher)(cs.ID) |
||||
return nil |
||||
} |
||||
} |
||||
return errors.New("unknown cipher: " + s) |
||||
} |
||||
|
||||
func (c cipher) MarshalYAML() (interface{}, error) { |
||||
return tls.CipherSuiteName((uint16)(c)), nil |
||||
} |
||||
|
||||
type curve tls.CurveID |
||||
|
||||
var curves = map[string]curve{ |
||||
"CurveP256": (curve)(tls.CurveP256), |
||||
"CurveP384": (curve)(tls.CurveP384), |
||||
"CurveP521": (curve)(tls.CurveP521), |
||||
"X25519": (curve)(tls.X25519), |
||||
} |
||||
|
||||
func (c *curve) UnmarshalYAML(unmarshal func(interface{}) error) error { |
||||
var s string |
||||
err := unmarshal((*string)(&s)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if curveid, ok := curves[s]; ok { |
||||
*c = curveid |
||||
return nil |
||||
} |
||||
return errors.New("unknown curve: " + s) |
||||
} |
||||
|
||||
func (c *curve) MarshalYAML() (interface{}, error) { |
||||
for s, curveid := range curves { |
||||
if *c == curveid { |
||||
return s, nil |
||||
} |
||||
} |
||||
return fmt.Sprintf("%v", c), nil |
||||
} |
||||
|
||||
type tlsVersion uint16 |
||||
|
||||
var tlsVersions = map[string]tlsVersion{ |
||||
"TLS13": (tlsVersion)(tls.VersionTLS13), |
||||
"TLS12": (tlsVersion)(tls.VersionTLS12), |
||||
"TLS11": (tlsVersion)(tls.VersionTLS11), |
||||
"TLS10": (tlsVersion)(tls.VersionTLS10), |
||||
} |
||||
|
||||
func (tv *tlsVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { |
||||
var s string |
||||
err := unmarshal((*string)(&s)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if v, ok := tlsVersions[s]; ok { |
||||
*tv = v |
||||
return nil |
||||
} |
||||
return errors.New("unknown TLS version: " + s) |
||||
} |
||||
|
||||
func (tv *tlsVersion) MarshalYAML() (interface{}, error) { |
||||
for s, v := range tlsVersions { |
||||
if *tv == v { |
||||
return s, nil |
||||
} |
||||
} |
||||
return fmt.Sprintf("%v", tv), nil |
||||
} |
@ -0,0 +1,70 @@
|
||||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package https |
||||
|
||||
import ( |
||||
"net/http" |
||||
|
||||
"github.com/go-kit/kit/log" |
||||
"golang.org/x/crypto/bcrypt" |
||||
) |
||||
|
||||
func validateUsers(configPath string) error { |
||||
c, err := getConfig(configPath) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
for _, p := range c.Users { |
||||
_, err = bcrypt.Cost([]byte(p)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
type userAuthRoundtrip struct { |
||||
tlsConfigPath string |
||||
handler http.Handler |
||||
logger log.Logger |
||||
} |
||||
|
||||
func (u *userAuthRoundtrip) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
c, err := getConfig(u.tlsConfigPath) |
||||
if err != nil { |
||||
u.logger.Log("msg", "Unable to parse configuration", "err", err) |
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) |
||||
return |
||||
} |
||||
|
||||
if len(c.Users) == 0 { |
||||
u.handler.ServeHTTP(w, r) |
||||
return |
||||
} |
||||
|
||||
user, pass, auth := r.BasicAuth() |
||||
if auth { |
||||
if hashedPassword, ok := c.Users[user]; ok { |
||||
if err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass)); err == nil { |
||||
u.handler.ServeHTTP(w, r) |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
w.Header().Set("WWW-Authenticate", "Basic") |
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) |
||||
} |
@ -0,0 +1,6 @@
|
||||
# Minimal TLS configuration example. Additionally, a certificate and a key file |
||||
# are needed. |
||||
tls_server_config: |
||||
cert_file: server.crt |
||||
key_file: server.key |
||||
|
@ -0,0 +1,35 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bcrypt |
||||
|
||||
import "encoding/base64" |
||||
|
||||
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" |
||||
|
||||
var bcEncoding = base64.NewEncoding(alphabet) |
||||
|
||||
func base64Encode(src []byte) []byte { |
||||
n := bcEncoding.EncodedLen(len(src)) |
||||
dst := make([]byte, n) |
||||
bcEncoding.Encode(dst, src) |
||||
for dst[n-1] == '=' { |
||||
n-- |
||||
} |
||||
return dst[:n] |
||||
} |
||||
|
||||
func base64Decode(src []byte) ([]byte, error) { |
||||
numOfEquals := 4 - (len(src) % 4) |
||||
for i := 0; i < numOfEquals; i++ { |
||||
src = append(src, '=') |
||||
} |
||||
|
||||
dst := make([]byte, bcEncoding.DecodedLen(len(src))) |
||||
n, err := bcEncoding.Decode(dst, src) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return dst[:n], nil |
||||
} |
@ -0,0 +1,295 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
|
||||
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
|
||||
package bcrypt // import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
// The code is a port of Provos and Mazières's C implementation.
|
||||
import ( |
||||
"crypto/rand" |
||||
"crypto/subtle" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"strconv" |
||||
|
||||
"golang.org/x/crypto/blowfish" |
||||
) |
||||
|
||||
const ( |
||||
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
|
||||
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
|
||||
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
|
||||
) |
||||
|
||||
// The error returned from CompareHashAndPassword when a password and hash do
|
||||
// not match.
|
||||
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password") |
||||
|
||||
// The error returned from CompareHashAndPassword when a hash is too short to
|
||||
// be a bcrypt hash.
|
||||
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password") |
||||
|
||||
// The error returned from CompareHashAndPassword when a hash was created with
|
||||
// a bcrypt algorithm newer than this implementation.
|
||||
type HashVersionTooNewError byte |
||||
|
||||
func (hv HashVersionTooNewError) Error() string { |
||||
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion) |
||||
} |
||||
|
||||
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
|
||||
type InvalidHashPrefixError byte |
||||
|
||||
func (ih InvalidHashPrefixError) Error() string { |
||||
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih)) |
||||
} |
||||
|
||||
type InvalidCostError int |
||||
|
||||
func (ic InvalidCostError) Error() string { |
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) |
||||
} |
||||
|
||||
const ( |
||||
majorVersion = '2' |
||||
minorVersion = 'a' |
||||
maxSaltSize = 16 |
||||
maxCryptedHashSize = 23 |
||||
encodedSaltSize = 22 |
||||
encodedHashSize = 31 |
||||
minHashSize = 59 |
||||
) |
||||
|
||||
// magicCipherData is an IV for the 64 Blowfish encryption calls in
|
||||
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
|
||||
var magicCipherData = []byte{ |
||||
0x4f, 0x72, 0x70, 0x68, |
||||
0x65, 0x61, 0x6e, 0x42, |
||||
0x65, 0x68, 0x6f, 0x6c, |
||||
0x64, 0x65, 0x72, 0x53, |
||||
0x63, 0x72, 0x79, 0x44, |
||||
0x6f, 0x75, 0x62, 0x74, |
||||
} |
||||
|
||||
type hashed struct { |
||||
hash []byte |
||||
salt []byte |
||||
cost int // allowed range is MinCost to MaxCost
|
||||
major byte |
||||
minor byte |
||||
} |
||||
|
||||
// GenerateFromPassword returns the bcrypt hash of the password at the given
|
||||
// cost. If the cost given is less than MinCost, the cost will be set to
|
||||
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
|
||||
// to compare the returned hashed password with its cleartext version.
|
||||
func GenerateFromPassword(password []byte, cost int) ([]byte, error) { |
||||
p, err := newFromPassword(password, cost) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return p.Hash(), nil |
||||
} |
||||
|
||||
// CompareHashAndPassword compares a bcrypt hashed password with its possible
|
||||
// plaintext equivalent. Returns nil on success, or an error on failure.
|
||||
func CompareHashAndPassword(hashedPassword, password []byte) error { |
||||
p, err := newFromHash(hashedPassword) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
otherHash, err := bcrypt(password, p.cost, p.salt) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} |
||||
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { |
||||
return nil |
||||
} |
||||
|
||||
return ErrMismatchedHashAndPassword |
||||
} |
||||
|
||||
// Cost returns the hashing cost used to create the given hashed
|
||||
// password. When, in the future, the hashing cost of a password system needs
|
||||
// to be increased in order to adjust for greater computational power, this
|
||||
// function allows one to establish which passwords need to be updated.
|
||||
func Cost(hashedPassword []byte) (int, error) { |
||||
p, err := newFromHash(hashedPassword) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return p.cost, nil |
||||
} |
||||
|
||||
func newFromPassword(password []byte, cost int) (*hashed, error) { |
||||
if cost < MinCost { |
||||
cost = DefaultCost |
||||
} |
||||
p := new(hashed) |
||||
p.major = majorVersion |
||||
p.minor = minorVersion |
||||
|
||||
err := checkCost(cost) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
p.cost = cost |
||||
|
||||
unencodedSalt := make([]byte, maxSaltSize) |
||||
_, err = io.ReadFull(rand.Reader, unencodedSalt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
p.salt = base64Encode(unencodedSalt) |
||||
hash, err := bcrypt(password, p.cost, p.salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
p.hash = hash |
||||
return p, err |
||||
} |
||||
|
||||
func newFromHash(hashedSecret []byte) (*hashed, error) { |
||||
if len(hashedSecret) < minHashSize { |
||||
return nil, ErrHashTooShort |
||||
} |
||||
p := new(hashed) |
||||
n, err := p.decodeVersion(hashedSecret) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
hashedSecret = hashedSecret[n:] |
||||
n, err = p.decodeCost(hashedSecret) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
hashedSecret = hashedSecret[n:] |
||||
|
||||
// The "+2" is here because we'll have to append at most 2 '=' to the salt
|
||||
// when base64 decoding it in expensiveBlowfishSetup().
|
||||
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) |
||||
copy(p.salt, hashedSecret[:encodedSaltSize]) |
||||
|
||||
hashedSecret = hashedSecret[encodedSaltSize:] |
||||
p.hash = make([]byte, len(hashedSecret)) |
||||
copy(p.hash, hashedSecret) |
||||
|
||||
return p, nil |
||||
} |
||||
|
||||
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) { |
||||
cipherData := make([]byte, len(magicCipherData)) |
||||
copy(cipherData, magicCipherData) |
||||
|
||||
c, err := expensiveBlowfishSetup(password, uint32(cost), salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for i := 0; i < 24; i += 8 { |
||||
for j := 0; j < 64; j++ { |
||||
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8]) |
||||
} |
||||
} |
||||
|
||||
// Bug compatibility with C bcrypt implementations. We only encode 23 of
|
||||
// the 24 bytes encrypted.
|
||||
hsh := base64Encode(cipherData[:maxCryptedHashSize]) |
||||
return hsh, nil |
||||
} |
||||
|
||||
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) { |
||||
csalt, err := base64Decode(salt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// Bug compatibility with C bcrypt implementations. They use the trailing
|
||||
// NULL in the key string during expansion.
|
||||
// We copy the key to prevent changing the underlying array.
|
||||
ckey := append(key[:len(key):len(key)], 0) |
||||
|
||||
c, err := blowfish.NewSaltedCipher(ckey, csalt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var i, rounds uint64 |
||||
rounds = 1 << cost |
||||
for i = 0; i < rounds; i++ { |
||||
blowfish.ExpandKey(ckey, c) |
||||
blowfish.ExpandKey(csalt, c) |
||||
} |
||||
|
||||
return c, nil |
||||
} |
||||
|
||||
func (p *hashed) Hash() []byte { |
||||
arr := make([]byte, 60) |
||||
arr[0] = '$' |
||||
arr[1] = p.major |
||||
n := 2 |
||||
if p.minor != 0 { |
||||
arr[2] = p.minor |
||||
n = 3 |
||||
} |
||||
arr[n] = '$' |
||||
n++ |
||||
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost))) |
||||
n += 2 |
||||
arr[n] = '$' |
||||
n++ |
||||
copy(arr[n:], p.salt) |
||||
n += encodedSaltSize |
||||
copy(arr[n:], p.hash) |
||||
n += encodedHashSize |
||||
return arr[:n] |
||||
} |
||||
|
||||
func (p *hashed) decodeVersion(sbytes []byte) (int, error) { |
||||
if sbytes[0] != '$' { |
||||
return -1, InvalidHashPrefixError(sbytes[0]) |
||||
} |
||||
if sbytes[1] > majorVersion { |
||||
return -1, HashVersionTooNewError(sbytes[1]) |
||||
} |
||||
p.major = sbytes[1] |
||||
n := 3 |
||||
if sbytes[2] != '$' { |
||||
p.minor = sbytes[2] |
||||
n++ |
||||
} |
||||
return n, nil |
||||
} |
||||
|
||||
// sbytes should begin where decodeVersion left off.
|
||||
func (p *hashed) decodeCost(sbytes []byte) (int, error) { |
||||
cost, err := strconv.Atoi(string(sbytes[0:2])) |
||||
if err != nil { |
||||
return -1, err |
||||
} |
||||
err = checkCost(cost) |
||||
if err != nil { |
||||
return -1, err |
||||
} |
||||
p.cost = cost |
||||
return 3, nil |
||||
} |
||||
|
||||
func (p *hashed) String() string { |
||||
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) |
||||
} |
||||
|
||||
func checkCost(cost int) error { |
||||
if cost < MinCost || cost > MaxCost { |
||||
return InvalidCostError(cost) |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,159 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blowfish |
||||
|
||||
// getNextWord returns the next big-endian uint32 value from the byte slice
|
||||
// at the given position in a circular manner, updating the position.
|
||||
func getNextWord(b []byte, pos *int) uint32 { |
||||
var w uint32 |
||||
j := *pos |
||||
for i := 0; i < 4; i++ { |
||||
w = w<<8 | uint32(b[j]) |
||||
j++ |
||||
if j >= len(b) { |
||||
j = 0 |
||||
} |
||||
} |
||||
*pos = j |
||||
return w |
||||
} |
||||
|
||||
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
|
||||
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
|
||||
// pi and substitution tables for calls to Encrypt. This is used, primarily,
|
||||
// by the bcrypt package to reuse the Blowfish key schedule during its
|
||||
// set up. It's unlikely that you need to use this directly.
|
||||
func ExpandKey(key []byte, c *Cipher) { |
||||
j := 0 |
||||
for i := 0; i < 18; i++ { |
||||
// Using inlined getNextWord for performance.
|
||||
var d uint32 |
||||
for k := 0; k < 4; k++ { |
||||
d = d<<8 | uint32(key[j]) |
||||
j++ |
||||
if j >= len(key) { |
||||
j = 0 |
||||
} |
||||
} |
||||
c.p[i] ^= d |
||||
} |
||||
|
||||
var l, r uint32 |
||||
for i := 0; i < 18; i += 2 { |
||||
l, r = encryptBlock(l, r, c) |
||||
c.p[i], c.p[i+1] = l, r |
||||
} |
||||
|
||||
for i := 0; i < 256; i += 2 { |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s0[i], c.s0[i+1] = l, r |
||||
} |
||||
for i := 0; i < 256; i += 2 { |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s1[i], c.s1[i+1] = l, r |
||||
} |
||||
for i := 0; i < 256; i += 2 { |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s2[i], c.s2[i+1] = l, r |
||||
} |
||||
for i := 0; i < 256; i += 2 { |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s3[i], c.s3[i+1] = l, r |
||||
} |
||||
} |
||||
|
||||
// This is similar to ExpandKey, but folds the salt during the key
|
||||
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
|
||||
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
|
||||
// and specializing it here is useful.
|
||||
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { |
||||
j := 0 |
||||
for i := 0; i < 18; i++ { |
||||
c.p[i] ^= getNextWord(key, &j) |
||||
} |
||||
|
||||
j = 0 |
||||
var l, r uint32 |
||||
for i := 0; i < 18; i += 2 { |
||||
l ^= getNextWord(salt, &j) |
||||
r ^= getNextWord(salt, &j) |
||||
l, r = encryptBlock(l, r, c) |
||||
c.p[i], c.p[i+1] = l, r |
||||
} |
||||
|
||||
for i := 0; i < 256; i += 2 { |
||||
l ^= getNextWord(salt, &j) |
||||
r ^= getNextWord(salt, &j) |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s0[i], c.s0[i+1] = l, r |
||||
} |
||||
|
||||
for i := 0; i < 256; i += 2 { |
||||
l ^= getNextWord(salt, &j) |
||||
r ^= getNextWord(salt, &j) |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s1[i], c.s1[i+1] = l, r |
||||
} |
||||
|
||||
for i := 0; i < 256; i += 2 { |
||||
l ^= getNextWord(salt, &j) |
||||
r ^= getNextWord(salt, &j) |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s2[i], c.s2[i+1] = l, r |
||||
} |
||||
|
||||
for i := 0; i < 256; i += 2 { |
||||
l ^= getNextWord(salt, &j) |
||||
r ^= getNextWord(salt, &j) |
||||
l, r = encryptBlock(l, r, c) |
||||
c.s3[i], c.s3[i+1] = l, r |
||||
} |
||||
} |
||||
|
||||
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { |
||||
xl, xr := l, r |
||||
xl ^= c.p[0] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] |
||||
xr ^= c.p[17] |
||||
return xr, xl |
||||
} |
||||
|
||||
func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { |
||||
xl, xr := l, r |
||||
xl ^= c.p[17] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] |
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] |
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] |
||||
xr ^= c.p[0] |
||||
return xr, xl |
||||
} |
@ -0,0 +1,99 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||
//
|
||||
// Blowfish is a legacy cipher and its short block size makes it vulnerable to
|
||||
// birthday bound attacks (see https://sweet32.info). It should only be used
|
||||
// where compatibility with legacy systems, not security, is the goal.
|
||||
//
|
||||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
|
||||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
|
||||
// golang.org/x/crypto/chacha20poly1305).
|
||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||
|
||||
// The code is a port of Bruce Schneier's C implementation.
|
||||
// See https://www.schneier.com/blowfish.html.
|
||||
|
||||
import "strconv" |
||||
|
||||
// The Blowfish block size in bytes.
|
||||
const BlockSize = 8 |
||||
|
||||
// A Cipher is an instance of Blowfish encryption using a particular key.
|
||||
type Cipher struct { |
||||
p [18]uint32 |
||||
s0, s1, s2, s3 [256]uint32 |
||||
} |
||||
|
||||
type KeySizeError int |
||||
|
||||
func (k KeySizeError) Error() string { |
||||
return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) |
||||
} |
||||
|
||||
// NewCipher creates and returns a Cipher.
|
||||
// The key argument should be the Blowfish key, from 1 to 56 bytes.
|
||||
func NewCipher(key []byte) (*Cipher, error) { |
||||
var result Cipher |
||||
if k := len(key); k < 1 || k > 56 { |
||||
return nil, KeySizeError(k) |
||||
} |
||||
initCipher(&result) |
||||
ExpandKey(key, &result) |
||||
return &result, nil |
||||
} |
||||
|
||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||
// bytes.
|
||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) { |
||||
if len(salt) == 0 { |
||||
return NewCipher(key) |
||||
} |
||||
var result Cipher |
||||
if k := len(key); k < 1 { |
||||
return nil, KeySizeError(k) |
||||
} |
||||
initCipher(&result) |
||||
expandKeyWithSalt(key, salt, &result) |
||||
return &result, nil |
||||
} |
||||
|
||||
// BlockSize returns the Blowfish block size, 8 bytes.
|
||||
// It is necessary to satisfy the Block interface in the
|
||||
// package "crypto/cipher".
|
||||
func (c *Cipher) BlockSize() int { return BlockSize } |
||||
|
||||
// Encrypt encrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) { |
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) |
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) |
||||
l, r = encryptBlock(l, r, c) |
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) |
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) |
||||
} |
||||
|
||||
// Decrypt decrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) { |
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) |
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) |
||||
l, r = decryptBlock(l, r, c) |
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) |
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) |
||||
} |
||||
|
||||
func initCipher(c *Cipher) { |
||||
copy(c.p[0:], p[0:]) |
||||
copy(c.s0[0:], s0[0:]) |
||||
copy(c.s1[0:], s1[0:]) |
||||
copy(c.s2[0:], s2[0:]) |
||||
copy(c.s3[0:], s3[0:]) |
||||
} |
@ -0,0 +1,199 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The startup permutation array and substitution boxes.
|
||||
// They are the hexadecimal digits of PI; see:
|
||||
// https://www.schneier.com/code/constants.txt.
|
||||
|
||||
package blowfish |
||||
|
||||
var s0 = [256]uint32{ |
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, |
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, |
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, |
||||
0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, |
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, |
||||
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, |
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, |
||||
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, |
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, |
||||
0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, |
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, |
||||
0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, |
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, |
||||
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, |
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, |
||||
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, |
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, |
||||
0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, |
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, |
||||
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, |
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, |
||||
0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, |
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, |
||||
0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, |
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, |
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, |
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, |
||||
0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, |
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, |
||||
0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, |
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, |
||||
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, |
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, |
||||
0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, |
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, |
||||
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, |
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, |
||||
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, |
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, |
||||
0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, |
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, |
||||
0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, |
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, |
||||
} |
||||
|
||||
var s1 = [256]uint32{ |
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, |
||||
0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, |
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, |
||||
0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, |
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, |
||||
0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, |
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, |
||||
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, |
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, |
||||
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, |
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, |
||||
0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, |
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, |
||||
0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, |
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, |
||||
0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, |
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, |
||||
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, |
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, |
||||
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, |
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, |
||||
0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, |
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, |
||||
0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, |
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, |
||||
0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, |
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, |
||||
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, |
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, |
||||
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, |
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, |
||||
0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, |
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, |
||||
0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, |
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, |
||||
0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, |
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, |
||||
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, |
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, |
||||
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, |
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, |
||||
0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, |
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, |
||||
} |
||||
|
||||
var s2 = [256]uint32{ |
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, |
||||
0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, |
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, |
||||
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, |
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, |
||||
0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, |
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, |
||||
0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, |
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, |
||||
0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, |
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, |
||||
0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, |
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, |
||||
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, |
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, |
||||
0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, |
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, |
||||
0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, |
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, |
||||
0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, |
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, |
||||
0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, |
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, |
||||
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, |
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, |
||||
0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, |
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, |
||||
0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, |
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, |
||||
0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, |
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, |
||||
0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, |
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, |
||||
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, |
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, |
||||
0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, |
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, |
||||
0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, |
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, |
||||
0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, |
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, |
||||
0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, |
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, |
||||
} |
||||
|
||||
var s3 = [256]uint32{ |
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, |
||||
0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, |
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, |
||||
0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, |
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, |
||||
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, |
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, |
||||
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, |
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, |
||||
0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, |
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, |
||||
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, |
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, |
||||
0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, |
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, |
||||
0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, |
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, |
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, |
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, |
||||
0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, |
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, |
||||
0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, |
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, |
||||
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, |
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, |
||||
0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, |
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, |
||||
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, |
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, |
||||
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, |
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, |
||||
0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, |
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, |
||||
0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, |
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, |
||||
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, |
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, |
||||
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, |
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, |
||||
0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, |
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, |
||||
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, |
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, |
||||
} |
||||
|
||||
var p = [18]uint32{ |
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, |
||||
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, |
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
#include "textflag.h" |
||||
|
||||
TEXT ·use(SB),NOSPLIT,$0 |
||||
RET |
@ -0,0 +1,30 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// |
||||
// System call support for 386, Plan 9 |
||||
// |
||||
|
||||
// Just jump to package syscall's implementation for all these functions. |
||||
// The runtime may know about them. |
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32 |
||||
JMP syscall·Syscall(SB) |
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44 |
||||
JMP syscall·Syscall6(SB) |
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
||||
JMP syscall·RawSyscall(SB) |
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
||||
JMP syscall·RawSyscall6(SB) |
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36 |
||||
JMP syscall·seek(SB) |
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$4-4 |
||||
JMP syscall·exit(SB) |
@ -0,0 +1,30 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// |
||||
// System call support for amd64, Plan 9 |
||||
// |
||||
|
||||
// Just jump to package syscall's implementation for all these functions. |
||||
// The runtime may know about them. |
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-64 |
||||
JMP syscall·Syscall(SB) |
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-88 |
||||
JMP syscall·Syscall6(SB) |
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
||||
JMP syscall·RawSyscall(SB) |
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
||||
JMP syscall·RawSyscall6(SB) |
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-56 |
||||
JMP syscall·seek(SB) |
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$8-8 |
||||
JMP syscall·exit(SB) |
@ -0,0 +1,25 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// System call support for plan9 on arm |
||||
|
||||
// Just jump to package syscall's implementation for all these functions. |
||||
// The runtime may know about them. |
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32 |
||||
JMP syscall·Syscall(SB) |
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44 |
||||
JMP syscall·Syscall6(SB) |
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
||||
JMP syscall·RawSyscall(SB) |
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
||||
JMP syscall·RawSyscall6(SB) |
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36 |
||||
JMP syscall·exit(SB) |
@ -0,0 +1,70 @@
|
||||
package plan9 |
||||
|
||||
// Plan 9 Constants
|
||||
|
||||
// Open modes
|
||||
const ( |
||||
O_RDONLY = 0 |
||||
O_WRONLY = 1 |
||||
O_RDWR = 2 |
||||
O_TRUNC = 16 |
||||
O_CLOEXEC = 32 |
||||
O_EXCL = 0x1000 |
||||
) |
||||
|
||||
// Rfork flags
|
||||
const ( |
||||
RFNAMEG = 1 << 0 |
||||
RFENVG = 1 << 1 |
||||
RFFDG = 1 << 2 |
||||
RFNOTEG = 1 << 3 |
||||
RFPROC = 1 << 4 |
||||
RFMEM = 1 << 5 |
||||
RFNOWAIT = 1 << 6 |
||||
RFCNAMEG = 1 << 10 |
||||
RFCENVG = 1 << 11 |
||||
RFCFDG = 1 << 12 |
||||
RFREND = 1 << 13 |
||||
RFNOMNT = 1 << 14 |
||||
) |
||||
|
||||
// Qid.Type bits
|
||||
const ( |
||||
QTDIR = 0x80 |
||||
QTAPPEND = 0x40 |
||||
QTEXCL = 0x20 |
||||
QTMOUNT = 0x10 |
||||
QTAUTH = 0x08 |
||||
QTTMP = 0x04 |
||||
QTFILE = 0x00 |
||||
) |
||||
|
||||
// Dir.Mode bits
|
||||
const ( |
||||
DMDIR = 0x80000000 |
||||
DMAPPEND = 0x40000000 |
||||
DMEXCL = 0x20000000 |
||||
DMMOUNT = 0x10000000 |
||||
DMAUTH = 0x08000000 |
||||
DMTMP = 0x04000000 |
||||
DMREAD = 0x4 |
||||
DMWRITE = 0x2 |
||||
DMEXEC = 0x1 |
||||
) |
||||
|
||||
const ( |
||||
STATMAX = 65535 |
||||
ERRMAX = 128 |
||||
STATFIXLEN = 49 |
||||
) |
||||
|
||||
// Mount and bind flags
|
||||
const ( |
||||
MREPL = 0x0000 |
||||
MBEFORE = 0x0001 |
||||
MAFTER = 0x0002 |
||||
MORDER = 0x0003 |
||||
MCREATE = 0x0004 |
||||
MCACHE = 0x0010 |
||||
MMASK = 0x0017 |
||||
) |
@ -0,0 +1,212 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 directory marshalling. See intro(5).
|
||||
|
||||
package plan9 |
||||
|
||||
import "errors" |
||||
|
||||
var ( |
||||
ErrShortStat = errors.New("stat buffer too short") |
||||
ErrBadStat = errors.New("malformed stat buffer") |
||||
ErrBadName = errors.New("bad character in file name") |
||||
) |
||||
|
||||
// A Qid represents a 9P server's unique identification for a file.
|
||||
type Qid struct { |
||||
Path uint64 // the file server's unique identification for the file
|
||||
Vers uint32 // version number for given Path
|
||||
Type uint8 // the type of the file (plan9.QTDIR for example)
|
||||
} |
||||
|
||||
// A Dir contains the metadata for a file.
|
||||
type Dir struct { |
||||
// system-modified data
|
||||
Type uint16 // server type
|
||||
Dev uint32 // server subtype
|
||||
|
||||
// file data
|
||||
Qid Qid // unique id from server
|
||||
Mode uint32 // permissions
|
||||
Atime uint32 // last read time
|
||||
Mtime uint32 // last write time
|
||||
Length int64 // file length
|
||||
Name string // last element of path
|
||||
Uid string // owner name
|
||||
Gid string // group name
|
||||
Muid string // last modifier name
|
||||
} |
||||
|
||||
var nullDir = Dir{ |
||||
Type: ^uint16(0), |
||||
Dev: ^uint32(0), |
||||
Qid: Qid{ |
||||
Path: ^uint64(0), |
||||
Vers: ^uint32(0), |
||||
Type: ^uint8(0), |
||||
}, |
||||
Mode: ^uint32(0), |
||||
Atime: ^uint32(0), |
||||
Mtime: ^uint32(0), |
||||
Length: ^int64(0), |
||||
} |
||||
|
||||
// Null assigns special "don't touch" values to members of d to
|
||||
// avoid modifying them during plan9.Wstat.
|
||||
func (d *Dir) Null() { *d = nullDir } |
||||
|
||||
// Marshal encodes a 9P stat message corresponding to d into b
|
||||
//
|
||||
// If there isn't enough space in b for a stat message, ErrShortStat is returned.
|
||||
func (d *Dir) Marshal(b []byte) (n int, err error) { |
||||
n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid) |
||||
if n > len(b) { |
||||
return n, ErrShortStat |
||||
} |
||||
|
||||
for _, c := range d.Name { |
||||
if c == '/' { |
||||
return n, ErrBadName |
||||
} |
||||
} |
||||
|
||||
b = pbit16(b, uint16(n)-2) |
||||
b = pbit16(b, d.Type) |
||||
b = pbit32(b, d.Dev) |
||||
b = pbit8(b, d.Qid.Type) |
||||
b = pbit32(b, d.Qid.Vers) |
||||
b = pbit64(b, d.Qid.Path) |
||||
b = pbit32(b, d.Mode) |
||||
b = pbit32(b, d.Atime) |
||||
b = pbit32(b, d.Mtime) |
||||
b = pbit64(b, uint64(d.Length)) |
||||
b = pstring(b, d.Name) |
||||
b = pstring(b, d.Uid) |
||||
b = pstring(b, d.Gid) |
||||
b = pstring(b, d.Muid) |
||||
|
||||
return n, nil |
||||
} |
||||
|
||||
// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
|
||||
//
|
||||
// If b is too small to hold a valid stat message, ErrShortStat is returned.
|
||||
//
|
||||
// If the stat message itself is invalid, ErrBadStat is returned.
|
||||
func UnmarshalDir(b []byte) (*Dir, error) { |
||||
if len(b) < STATFIXLEN { |
||||
return nil, ErrShortStat |
||||
} |
||||
size, buf := gbit16(b) |
||||
if len(b) != int(size)+2 { |
||||
return nil, ErrBadStat |
||||
} |
||||
b = buf |
||||
|
||||
var d Dir |
||||
d.Type, b = gbit16(b) |
||||
d.Dev, b = gbit32(b) |
||||
d.Qid.Type, b = gbit8(b) |
||||
d.Qid.Vers, b = gbit32(b) |
||||
d.Qid.Path, b = gbit64(b) |
||||
d.Mode, b = gbit32(b) |
||||
d.Atime, b = gbit32(b) |
||||
d.Mtime, b = gbit32(b) |
||||
|
||||
n, b := gbit64(b) |
||||
d.Length = int64(n) |
||||
|
||||
var ok bool |
||||
if d.Name, b, ok = gstring(b); !ok { |
||||
return nil, ErrBadStat |
||||
} |
||||
if d.Uid, b, ok = gstring(b); !ok { |
||||
return nil, ErrBadStat |
||||
} |
||||
if d.Gid, b, ok = gstring(b); !ok { |
||||
return nil, ErrBadStat |
||||
} |
||||
if d.Muid, b, ok = gstring(b); !ok { |
||||
return nil, ErrBadStat |
||||
} |
||||
|
||||
return &d, nil |
||||
} |
||||
|
||||
// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
|
||||
func pbit8(b []byte, v uint8) []byte { |
||||
b[0] = byte(v) |
||||
return b[1:] |
||||
} |
||||
|
||||
// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit16(b []byte, v uint16) []byte { |
||||
b[0] = byte(v) |
||||
b[1] = byte(v >> 8) |
||||
return b[2:] |
||||
} |
||||
|
||||
// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit32(b []byte, v uint32) []byte { |
||||
b[0] = byte(v) |
||||
b[1] = byte(v >> 8) |
||||
b[2] = byte(v >> 16) |
||||
b[3] = byte(v >> 24) |
||||
return b[4:] |
||||
} |
||||
|
||||
// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit64(b []byte, v uint64) []byte { |
||||
b[0] = byte(v) |
||||
b[1] = byte(v >> 8) |
||||
b[2] = byte(v >> 16) |
||||
b[3] = byte(v >> 24) |
||||
b[4] = byte(v >> 32) |
||||
b[5] = byte(v >> 40) |
||||
b[6] = byte(v >> 48) |
||||
b[7] = byte(v >> 56) |
||||
return b[8:] |
||||
} |
||||
|
||||
// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
|
||||
// returning the remaining slice of b..
|
||||
func pstring(b []byte, s string) []byte { |
||||
b = pbit16(b, uint16(len(s))) |
||||
n := copy(b, s) |
||||
return b[n:] |
||||
} |
||||
|
||||
// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
|
||||
func gbit8(b []byte) (uint8, []byte) { |
||||
return uint8(b[0]), b[1:] |
||||
} |
||||
|
||||
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit16(b []byte) (uint16, []byte) { |
||||
return uint16(b[0]) | uint16(b[1])<<8, b[2:] |
||||
} |
||||
|
||||
// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit32(b []byte) (uint32, []byte) { |
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] |
||||
} |
||||
|
||||
// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit64(b []byte) (uint64, []byte) { |
||||
lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 |
||||
hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24 |
||||
return uint64(lo) | uint64(hi)<<32, b[8:] |
||||
} |
||||
|
||||
// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
|
||||
// It returns the string with the remaining slice of b and a boolean. If the length is
|
||||
// greater than the number of bytes in b, the boolean will be false.
|
||||
func gstring(b []byte) (string, []byte, bool) { |
||||
n, b := gbit16(b) |
||||
if int(n) > len(b) { |
||||
return "", b, false |
||||
} |
||||
return string(b[:n]), b[n:], true |
||||
} |
@ -0,0 +1,31 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 environment variables.
|
||||
|
||||
package plan9 |
||||
|
||||
import ( |
||||
"syscall" |
||||
) |
||||
|
||||
func Getenv(key string) (value string, found bool) { |
||||
return syscall.Getenv(key) |
||||
} |
||||
|
||||
func Setenv(key, value string) error { |
||||
return syscall.Setenv(key, value) |
||||
} |
||||
|
||||
func Clearenv() { |
||||
syscall.Clearenv() |
||||
} |
||||
|
||||
func Environ() []string { |
||||
return syscall.Environ() |
||||
} |
||||
|
||||
func Unsetenv(key string) error { |
||||
return syscall.Unsetenv(key) |
||||
} |
@ -0,0 +1,50 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package plan9 |
||||
|
||||
import "syscall" |
||||
|
||||
// Constants
|
||||
const ( |
||||
// Invented values to support what package os expects.
|
||||
O_CREAT = 0x02000 |
||||
O_APPEND = 0x00400 |
||||
O_NOCTTY = 0x00000 |
||||
O_NONBLOCK = 0x00000 |
||||
O_SYNC = 0x00000 |
||||
O_ASYNC = 0x00000 |
||||
|
||||
S_IFMT = 0x1f000 |
||||
S_IFIFO = 0x1000 |
||||
S_IFCHR = 0x2000 |
||||
S_IFDIR = 0x4000 |
||||
S_IFBLK = 0x6000 |
||||
S_IFREG = 0x8000 |
||||
S_IFLNK = 0xa000 |
||||
S_IFSOCK = 0xc000 |
||||
) |
||||
|
||||
// Errors
|
||||
var ( |
||||
EINVAL = syscall.NewError("bad arg in system call") |
||||
ENOTDIR = syscall.NewError("not a directory") |
||||
EISDIR = syscall.NewError("file is a directory") |
||||
ENOENT = syscall.NewError("file does not exist") |
||||
EEXIST = syscall.NewError("file already exists") |
||||
EMFILE = syscall.NewError("no free file descriptors") |
||||
EIO = syscall.NewError("i/o error") |
||||
ENAMETOOLONG = syscall.NewError("file name too long") |
||||
EINTR = syscall.NewError("interrupted") |
||||
EPERM = syscall.NewError("permission denied") |
||||
EBUSY = syscall.NewError("no free devices") |
||||
ETIMEDOUT = syscall.NewError("connection timed out") |
||||
EPLAN9 = syscall.NewError("not supported by plan 9") |
||||
|
||||
// The following errors do not correspond to any
|
||||
// Plan 9 system messages. Invented to support
|
||||
// what package os and others expect.
|
||||
EACCES = syscall.NewError("access permission denied") |
||||
EAFNOSUPPORT = syscall.NewError("address family not supported by protocol") |
||||
) |
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash |
||||
# Copyright 2009 The Go Authors. All rights reserved. |
||||
# Use of this source code is governed by a BSD-style |
||||
# license that can be found in the LICENSE file. |
||||
|
||||
# The plan9 package provides access to the raw system call |
||||
# interface of the underlying operating system. Porting Go to |
||||
# a new architecture/operating system combination requires |
||||
# some manual effort, though there are tools that automate |
||||
# much of the process. The auto-generated files have names |
||||
# beginning with z. |
||||
# |
||||
# This script runs or (given -n) prints suggested commands to generate z files |
||||
# for the current system. Running those commands is not automatic. |
||||
# This script is documentation more than anything else. |
||||
# |
||||
# * asm_${GOOS}_${GOARCH}.s |
||||
# |
||||
# This hand-written assembly file implements system call dispatch. |
||||
# There are three entry points: |
||||
# |
||||
# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); |
||||
# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); |
||||
# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); |
||||
# |
||||
# The first and second are the standard ones; they differ only in |
||||
# how many arguments can be passed to the kernel. |
||||
# The third is for low-level use by the ForkExec wrapper; |
||||
# unlike the first two, it does not call into the scheduler to |
||||
# let it know that a system call is running. |
||||
# |
||||
# * syscall_${GOOS}.go |
||||
# |
||||
# This hand-written Go file implements system calls that need |
||||
# special handling and lists "//sys" comments giving prototypes |
||||
# for ones that can be auto-generated. Mksyscall reads those |
||||
# comments to generate the stubs. |
||||
# |
||||
# * syscall_${GOOS}_${GOARCH}.go |
||||
# |
||||
# Same as syscall_${GOOS}.go except that it contains code specific |
||||
# to ${GOOS} on one particular architecture. |
||||
# |
||||
# * types_${GOOS}.c |
||||
# |
||||
# This hand-written C file includes standard C headers and then |
||||
# creates typedef or enum names beginning with a dollar sign |
||||
# (use of $ in variable names is a gcc extension). The hardest |
||||
# part about preparing this file is figuring out which headers to |
||||
# include and which symbols need to be #defined to get the |
||||
# actual data structures that pass through to the kernel system calls. |
||||
# Some C libraries present alternate versions for binary compatibility |
||||
# and translate them on the way in and out of system calls, but |
||||
# there is almost always a #define that can get the real ones. |
||||
# See types_darwin.c and types_linux.c for examples. |
||||
# |
||||
# * zerror_${GOOS}_${GOARCH}.go |
||||
# |
||||
# This machine-generated file defines the system's error numbers, |
||||
# error strings, and signal numbers. The generator is "mkerrors.sh". |
||||
# Usually no arguments are needed, but mkerrors.sh will pass its |
||||
# arguments on to godefs. |
||||
# |
||||
# * zsyscall_${GOOS}_${GOARCH}.go |
||||
# |
||||
# Generated by mksyscall.pl; see syscall_${GOOS}.go above. |
||||
# |
||||
# * zsysnum_${GOOS}_${GOARCH}.go |
||||
# |
||||
# Generated by mksysnum_${GOOS}. |
||||
# |
||||
# * ztypes_${GOOS}_${GOARCH}.go |
||||
# |
||||
# Generated by godefs; see types_${GOOS}.c above. |
||||
|
||||
GOOSARCH="${GOOS}_${GOARCH}" |
||||
|
||||
# defaults |
||||
mksyscall="go run mksyscall.go" |
||||
mkerrors="./mkerrors.sh" |
||||
zerrors="zerrors_$GOOSARCH.go" |
||||
mksysctl="" |
||||
zsysctl="zsysctl_$GOOSARCH.go" |
||||
mksysnum= |
||||
mktypes= |
||||
run="sh" |
||||
|
||||
case "$1" in |
||||
-syscalls) |
||||
for i in zsyscall*go |
||||
do |
||||
sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i |
||||
rm _$i |
||||
done |
||||
exit 0 |
||||
;; |
||||
-n) |
||||
run="cat" |
||||
shift |
||||
esac |
||||
|
||||
case "$#" in |
||||
0) |
||||
;; |
||||
*) |
||||
echo 'usage: mkall.sh [-n]' 1>&2 |
||||
exit 2 |
||||
esac |
||||
|
||||
case "$GOOSARCH" in |
||||
_* | *_ | _) |
||||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 |
||||
exit 1 |
||||
;; |
||||
plan9_386) |
||||
mkerrors= |
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,386" |
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" |
||||
mktypes="XXX" |
||||
;; |
||||
plan9_amd64) |
||||
mkerrors= |
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,amd64" |
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" |
||||
mktypes="XXX" |
||||
;; |
||||
plan9_arm) |
||||
mkerrors= |
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,arm" |
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h" |
||||
mktypes="XXX" |
||||
;; |
||||
*) |
||||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2 |
||||
exit 1 |
||||
;; |
||||
esac |
||||
|
||||
( |
||||
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi |
||||
case "$GOOS" in |
||||
plan9) |
||||
syscall_goos="syscall_$GOOS.go" |
||||
if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos |gofmt >zsyscall_$GOOSARCH.go"; fi |
||||
;; |
||||
esac |
||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi |
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi |
||||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi |
||||
) | $run |
@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env bash |
||||
# Copyright 2009 The Go Authors. All rights reserved. |
||||
# Use of this source code is governed by a BSD-style |
||||
# license that can be found in the LICENSE file. |
||||
|
||||
# Generate Go code listing errors and other #defined constant |
||||
# values (ENAMETOOLONG etc.), by asking the preprocessor |
||||
# about the definitions. |
||||
|
||||
unset LANG |
||||
export LC_ALL=C |
||||
export LC_CTYPE=C |
||||
|
||||
CC=${CC:-gcc} |
||||
|
||||
uname=$(uname) |
||||
|
||||
includes=' |
||||
#include <sys/types.h> |
||||
#include <sys/file.h> |
||||
#include <fcntl.h> |
||||
#include <dirent.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <netinet/ip.h> |
||||
#include <netinet/ip6.h> |
||||
#include <netinet/tcp.h> |
||||
#include <errno.h> |
||||
#include <sys/signal.h> |
||||
#include <signal.h> |
||||
#include <sys/resource.h> |
||||
' |
||||
|
||||
ccflags="$@" |
||||
|
||||
# Write go tool cgo -godefs input. |
||||
( |
||||
echo package plan9 |
||||
echo |
||||
echo '/*' |
||||
indirect="includes_$(uname)" |
||||
echo "${!indirect} $includes" |
||||
echo '*/' |
||||
echo 'import "C"' |
||||
echo |
||||
echo 'const (' |
||||
|
||||
# The gcc command line prints all the #defines |
||||
# it encounters while processing the input |
||||
echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags | |
||||
awk ' |
||||
$1 != "#define" || $2 ~ /\(/ || $3 == "" {next} |
||||
|
||||
$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers |
||||
$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next} |
||||
$2 ~ /^(SCM_SRCRT)$/ {next} |
||||
$2 ~ /^(MAP_FAILED)$/ {next} |
||||
|
||||
$2 !~ /^ETH_/ && |
||||
$2 !~ /^EPROC_/ && |
||||
$2 !~ /^EQUIV_/ && |
||||
$2 !~ /^EXPR_/ && |
||||
$2 ~ /^E[A-Z0-9_]+$/ || |
||||
$2 ~ /^B[0-9_]+$/ || |
||||
$2 ~ /^V[A-Z0-9]+$/ || |
||||
$2 ~ /^CS[A-Z0-9]/ || |
||||
$2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ || |
||||
$2 ~ /^IGN/ || |
||||
$2 ~ /^IX(ON|ANY|OFF)$/ || |
||||
$2 ~ /^IN(LCR|PCK)$/ || |
||||
$2 ~ /(^FLU?SH)|(FLU?SH$)/ || |
||||
$2 ~ /^C(LOCAL|READ)$/ || |
||||
$2 == "BRKINT" || |
||||
$2 == "HUPCL" || |
||||
$2 == "PENDIN" || |
||||
$2 == "TOSTOP" || |
||||
$2 ~ /^PAR/ || |
||||
$2 ~ /^SIG[^_]/ || |
||||
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ || |
||||
$2 ~ /^IN_/ || |
||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || |
||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ || |
||||
$2 == "ICMPV6_FILTER" || |
||||
$2 == "SOMAXCONN" || |
||||
$2 == "NAME_MAX" || |
||||
$2 == "IFNAMSIZ" || |
||||
$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ || |
||||
$2 ~ /^SYSCTL_VERS/ || |
||||
$2 ~ /^(MS|MNT)_/ || |
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || |
||||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ || |
||||
$2 ~ /^LINUX_REBOOT_CMD_/ || |
||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || |
||||
$2 !~ "NLA_TYPE_MASK" && |
||||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ || |
||||
$2 ~ /^SIOC/ || |
||||
$2 ~ /^TIOC/ || |
||||
$2 !~ "RTF_BITS" && |
||||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || |
||||
$2 ~ /^BIOC/ || |
||||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ || |
||||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ || |
||||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || |
||||
$2 ~ /^CLONE_[A-Z_]+/ || |
||||
$2 !~ /^(BPF_TIMEVAL)$/ && |
||||
$2 ~ /^(BPF|DLT)_/ || |
||||
$2 !~ "WMESGLEN" && |
||||
$2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)} |
||||
$2 ~ /^__WCOREFLAG$/ {next} |
||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} |
||||
|
||||
{next} |
||||
' | sort |
||||
|
||||
echo ')' |
||||
) >_const.go |
||||
|
||||
# Pull out the error names for later. |
||||
errors=$( |
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags | |
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' | |
||||
sort |
||||
) |
||||
|
||||
# Pull out the signal names for later. |
||||
signals=$( |
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | |
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' | |
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' | |
||||
sort |
||||
) |
||||
|
||||
# Again, writing regexps to a file. |
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags | |
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' | |
||||
sort >_error.grep |
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags | |
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' | |
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' | |
||||
sort >_signal.grep |
||||
|
||||
echo '// mkerrors.sh' "$@" |
||||
echo '// Code generated by the command above; DO NOT EDIT.' |
||||
echo |
||||
go tool cgo -godefs -- "$@" _const.go >_error.out |
||||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep |
||||
echo |
||||
echo '// Errors' |
||||
echo 'const (' |
||||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/' |
||||
echo ')' |
||||
|
||||
echo |
||||
echo '// Signals' |
||||
echo 'const (' |
||||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/' |
||||
echo ')' |
||||
|
||||
# Run C program to print error and syscall strings. |
||||
( |
||||
echo -E " |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <errno.h> |
||||
#include <ctype.h> |
||||
#include <string.h> |
||||
#include <signal.h> |
||||
|
||||
#define nelem(x) (sizeof(x)/sizeof((x)[0])) |
||||
|
||||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below |
||||
|
||||
int errors[] = { |
||||
" |
||||
for i in $errors |
||||
do |
||||
echo -E ' '$i, |
||||
done |
||||
|
||||
echo -E " |
||||
}; |
||||
|
||||
int signals[] = { |
||||
" |
||||
for i in $signals |
||||
do |
||||
echo -E ' '$i, |
||||
done |
||||
|
||||
# Use -E because on some systems bash builtin interprets \n itself. |
||||
echo -E ' |
||||
}; |
||||
|
||||
static int |
||||
intcmp(const void *a, const void *b) |
||||
{ |
||||
return *(int*)a - *(int*)b; |
||||
} |
||||
|
||||
int |
||||
main(void) |
||||
{ |
||||
int i, j, e; |
||||
char buf[1024], *p; |
||||
|
||||
printf("\n\n// Error table\n"); |
||||
printf("var errors = [...]string {\n"); |
||||
qsort(errors, nelem(errors), sizeof errors[0], intcmp); |
||||
for(i=0; i<nelem(errors); i++) { |
||||
e = errors[i]; |
||||
if(i > 0 && errors[i-1] == e) |
||||
continue; |
||||
strcpy(buf, strerror(e)); |
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM. |
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) |
||||
buf[0] += a - A; |
||||
printf("\t%d: \"%s\",\n", e, buf); |
||||
} |
||||
printf("}\n\n"); |
||||
|
||||
printf("\n\n// Signal table\n"); |
||||
printf("var signals = [...]string {\n"); |
||||
qsort(signals, nelem(signals), sizeof signals[0], intcmp); |
||||
for(i=0; i<nelem(signals); i++) { |
||||
e = signals[i]; |
||||
if(i > 0 && signals[i-1] == e) |
||||
continue; |
||||
strcpy(buf, strsignal(e)); |
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM. |
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) |
||||
buf[0] += a - A; |
||||
// cut trailing : number. |
||||
p = strrchr(buf, ":"[0]); |
||||
if(p) |
||||
*p = '\0'; |
||||
printf("\t%d: \"%s\",\n", e, buf); |
||||
} |
||||
printf("}\n\n"); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
' |
||||
) >_errors.c |
||||
|
||||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out |
@ -0,0 +1,23 @@
|
||||
#!/bin/sh |
||||
# Copyright 2009 The Go Authors. All rights reserved. |
||||
# Use of this source code is governed by a BSD-style |
||||
# license that can be found in the LICENSE file. |
||||
|
||||
COMMAND="mksysnum_plan9.sh $@" |
||||
|
||||
cat <<EOF |
||||
// $COMMAND |
||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT |
||||
|
||||
package plan9 |
||||
|
||||
const( |
||||
EOF |
||||
|
||||
SP='[ ]' # space or tab |
||||
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \ |
||||
< $1 | grep -v SYS__ |
||||
|
||||
cat <<EOF |
||||
) |
||||
EOF |
@ -0,0 +1,21 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package plan9 |
||||
|
||||
import "syscall" |
||||
|
||||
func fixwd() { |
||||
syscall.Fixwd() |
||||
} |
||||
|
||||
func Getwd() (wd string, err error) { |
||||
return syscall.Getwd() |
||||
} |
||||
|
||||
func Chdir(path string) error { |
||||
return syscall.Chdir(path) |
||||
} |
@ -0,0 +1,23 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package plan9 |
||||
|
||||
func fixwd() { |
||||
} |
||||
|
||||
func Getwd() (wd string, err error) { |
||||
fd, err := open(".", O_RDONLY) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
defer Close(fd) |
||||
return Fd2path(fd) |
||||
} |
||||
|
||||
func Chdir(path string) error { |
||||
return chdir(path) |
||||
} |
@ -0,0 +1,30 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9,race
|
||||
|
||||
package plan9 |
||||
|
||||
import ( |
||||
"runtime" |
||||
"unsafe" |
||||
) |
||||
|
||||
const raceenabled = true |
||||
|
||||
func raceAcquire(addr unsafe.Pointer) { |
||||
runtime.RaceAcquire(addr) |
||||
} |
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) { |
||||
runtime.RaceReleaseMerge(addr) |
||||
} |
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) { |
||||
runtime.RaceReadRange(addr, len) |
||||
} |
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) { |
||||
runtime.RaceWriteRange(addr, len) |
||||
} |
@ -0,0 +1,25 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9,!race
|
||||
|
||||
package plan9 |
||||
|
||||
import ( |
||||
"unsafe" |
||||
) |
||||
|
||||
const raceenabled = false |
||||
|
||||
func raceAcquire(addr unsafe.Pointer) { |
||||
} |
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) { |
||||
} |
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) { |
||||
} |
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) { |
||||
} |
@ -0,0 +1,22 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9
|
||||
|
||||
package plan9 |
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 { |
||||
return "-" + itoa(-val) |
||||
} |
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1 |
||||
for val >= 10 { |
||||
buf[i] = byte(val%10 + '0') |
||||
i-- |
||||
val /= 10 |
||||
} |
||||
buf[i] = byte(val + '0') |
||||
return string(buf[i:]) |
||||
} |
@ -0,0 +1,116 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9
|
||||
|
||||
// Package plan9 contains an interface to the low-level operating system
|
||||
// primitives. OS details vary depending on the underlying system, and
|
||||
// by default, godoc will display the OS-specific documentation for the current
|
||||
// system. If you want godoc to display documentation for another
|
||||
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||
// to freebsd and $GOARCH to arm.
|
||||
//
|
||||
// The primary use of this package is inside other packages that provide a more
|
||||
// portable interface to the system, such as "os", "time" and "net". Use
|
||||
// those packages rather than this one if you can.
|
||||
//
|
||||
// For details of the functions and data types in this package consult
|
||||
// the manuals for the appropriate operating system.
|
||||
//
|
||||
// These calls return err == nil to indicate success; otherwise
|
||||
// err represents an operating system error describing the failure and
|
||||
// holds a value of type syscall.ErrorString.
|
||||
package plan9 // import "golang.org/x/sys/plan9"
|
||||
|
||||
import ( |
||||
"bytes" |
||||
"strings" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/internal/unsafeheader" |
||||
) |
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
// containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func ByteSliceFromString(s string) ([]byte, error) { |
||||
if strings.IndexByte(s, 0) != -1 { |
||||
return nil, EINVAL |
||||
} |
||||
a := make([]byte, len(s)+1) |
||||
copy(a, s) |
||||
return a, nil |
||||
} |
||||
|
||||
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||
// bytes containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func BytePtrFromString(s string) (*byte, error) { |
||||
a, err := ByteSliceFromString(s) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &a[0], nil |
||||
} |
||||
|
||||
// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
|
||||
// bytes after the NUL removed.
|
||||
func ByteSliceToString(s []byte) string { |
||||
if i := bytes.IndexByte(s, 0); i != -1 { |
||||
s = s[:i] |
||||
} |
||||
return string(s) |
||||
} |
||||
|
||||
// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
|
||||
// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
|
||||
// at a zero byte; if the zero byte is not present, the program may crash.
|
||||
func BytePtrToString(p *byte) string { |
||||
if p == nil { |
||||
return "" |
||||
} |
||||
if *p == 0 { |
||||
return "" |
||||
} |
||||
|
||||
// Find NUL terminator.
|
||||
n := 0 |
||||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ { |
||||
ptr = unsafe.Pointer(uintptr(ptr) + 1) |
||||
} |
||||
|
||||
var s []byte |
||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&s)) |
||||
h.Data = unsafe.Pointer(p) |
||||
h.Len = n |
||||
h.Cap = n |
||||
|
||||
return string(s) |
||||
} |
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
// See mksyscall.pl.
|
||||
var _zero uintptr |
||||
|
||||
func (ts *Timespec) Unix() (sec int64, nsec int64) { |
||||
return int64(ts.Sec), int64(ts.Nsec) |
||||
} |
||||
|
||||
func (tv *Timeval) Unix() (sec int64, nsec int64) { |
||||
return int64(tv.Sec), int64(tv.Usec) * 1000 |
||||
} |
||||
|
||||
func (ts *Timespec) Nano() int64 { |
||||
return int64(ts.Sec)*1e9 + int64(ts.Nsec) |
||||
} |
||||
|
||||
func (tv *Timeval) Nano() int64 { |
||||
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 |
||||
} |
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
//go:noescape
|
||||
func use(p unsafe.Pointer) |
@ -0,0 +1,349 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 system calls.
|
||||
// This file is compiled as ordinary Go code,
|
||||
// but it is also input to mksyscall,
|
||||
// which parses the //sys lines and generates system call stubs.
|
||||
// Note that sometimes we use a lowercase //sys name and
|
||||
// wrap it in our own nicer implementation.
|
||||
|
||||
package plan9 |
||||
|
||||
import ( |
||||
"bytes" |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
// A Note is a string describing a process note.
|
||||
// It implements the os.Signal interface.
|
||||
type Note string |
||||
|
||||
func (n Note) Signal() {} |
||||
|
||||
func (n Note) String() string { |
||||
return string(n) |
||||
} |
||||
|
||||
var ( |
||||
Stdin = 0 |
||||
Stdout = 1 |
||||
Stderr = 2 |
||||
) |
||||
|
||||
// For testing: clients can set this flag to force
|
||||
// creation of IPv6 sockets to return EAFNOSUPPORT.
|
||||
var SocketDisableIPv6 bool |
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString) |
||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString) |
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) |
||||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) |
||||
|
||||
func atoi(b []byte) (n uint) { |
||||
n = 0 |
||||
for i := 0; i < len(b); i++ { |
||||
n = n*10 + uint(b[i]-'0') |
||||
} |
||||
return |
||||
} |
||||
|
||||
func cstring(s []byte) string { |
||||
i := bytes.IndexByte(s, 0) |
||||
if i == -1 { |
||||
i = len(s) |
||||
} |
||||
return string(s[:i]) |
||||
} |
||||
|
||||
func errstr() string { |
||||
var buf [ERRMAX]byte |
||||
|
||||
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0) |
||||
|
||||
buf[len(buf)-1] = 0 |
||||
return cstring(buf[:]) |
||||
} |
||||
|
||||
// Implemented in assembly to import from runtime.
|
||||
func exit(code int) |
||||
|
||||
func Exit(code int) { exit(code) } |
||||
|
||||
func readnum(path string) (uint, error) { |
||||
var b [12]byte |
||||
|
||||
fd, e := Open(path, O_RDONLY) |
||||
if e != nil { |
||||
return 0, e |
||||
} |
||||
defer Close(fd) |
||||
|
||||
n, e := Pread(fd, b[:], 0) |
||||
|
||||
if e != nil { |
||||
return 0, e |
||||
} |
||||
|
||||
m := 0 |
||||
for ; m < n && b[m] == ' '; m++ { |
||||
} |
||||
|
||||
return atoi(b[m : n-1]), nil |
||||
} |
||||
|
||||
func Getpid() (pid int) { |
||||
n, _ := readnum("#c/pid") |
||||
return int(n) |
||||
} |
||||
|
||||
func Getppid() (ppid int) { |
||||
n, _ := readnum("#c/ppid") |
||||
return int(n) |
||||
} |
||||
|
||||
func Read(fd int, p []byte) (n int, err error) { |
||||
return Pread(fd, p, -1) |
||||
} |
||||
|
||||
func Write(fd int, p []byte) (n int, err error) { |
||||
return Pwrite(fd, p, -1) |
||||
} |
||||
|
||||
var ioSync int64 |
||||
|
||||
//sys fd2path(fd int, buf []byte) (err error)
|
||||
func Fd2path(fd int) (path string, err error) { |
||||
var buf [512]byte |
||||
|
||||
e := fd2path(fd, buf[:]) |
||||
if e != nil { |
||||
return "", e |
||||
} |
||||
return cstring(buf[:]), nil |
||||
} |
||||
|
||||
//sys pipe(p *[2]int32) (err error)
|
||||
func Pipe(p []int) (err error) { |
||||
if len(p) != 2 { |
||||
return syscall.ErrorString("bad arg in system call") |
||||
} |
||||
var pp [2]int32 |
||||
err = pipe(&pp) |
||||
p[0] = int(pp[0]) |
||||
p[1] = int(pp[1]) |
||||
return |
||||
} |
||||
|
||||
// Underlying system call writes to newoffset via pointer.
|
||||
// Implemented in assembly to avoid allocation.
|
||||
func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) |
||||
|
||||
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { |
||||
newoffset, e := seek(0, fd, offset, whence) |
||||
|
||||
if newoffset == -1 { |
||||
err = syscall.ErrorString(e) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func Mkdir(path string, mode uint32) (err error) { |
||||
fd, err := Create(path, O_RDONLY, DMDIR|mode) |
||||
|
||||
if fd != -1 { |
||||
Close(fd) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
type Waitmsg struct { |
||||
Pid int |
||||
Time [3]uint32 |
||||
Msg string |
||||
} |
||||
|
||||
func (w Waitmsg) Exited() bool { return true } |
||||
func (w Waitmsg) Signaled() bool { return false } |
||||
|
||||
func (w Waitmsg) ExitStatus() int { |
||||
if len(w.Msg) == 0 { |
||||
// a normal exit returns no message
|
||||
return 0 |
||||
} |
||||
return 1 |
||||
} |
||||
|
||||
//sys await(s []byte) (n int, err error)
|
||||
func Await(w *Waitmsg) (err error) { |
||||
var buf [512]byte |
||||
var f [5][]byte |
||||
|
||||
n, err := await(buf[:]) |
||||
|
||||
if err != nil || w == nil { |
||||
return |
||||
} |
||||
|
||||
nf := 0 |
||||
p := 0 |
||||
for i := 0; i < n && nf < len(f)-1; i++ { |
||||
if buf[i] == ' ' { |
||||
f[nf] = buf[p:i] |
||||
p = i + 1 |
||||
nf++ |
||||
} |
||||
} |
||||
f[nf] = buf[p:] |
||||
nf++ |
||||
|
||||
if nf != len(f) { |
||||
return syscall.ErrorString("invalid wait message") |
||||
} |
||||
w.Pid = int(atoi(f[0])) |
||||
w.Time[0] = uint32(atoi(f[1])) |
||||
w.Time[1] = uint32(atoi(f[2])) |
||||
w.Time[2] = uint32(atoi(f[3])) |
||||
w.Msg = cstring(f[4]) |
||||
if w.Msg == "''" { |
||||
// await() returns '' for no error
|
||||
w.Msg = "" |
||||
} |
||||
return |
||||
} |
||||
|
||||
func Unmount(name, old string) (err error) { |
||||
fixwd() |
||||
oldp, err := BytePtrFromString(old) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
oldptr := uintptr(unsafe.Pointer(oldp)) |
||||
|
||||
var r0 uintptr |
||||
var e syscall.ErrorString |
||||
|
||||
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
|
||||
if name == "" { |
||||
r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0) |
||||
} else { |
||||
namep, err := BytePtrFromString(name) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0) |
||||
} |
||||
|
||||
if int32(r0) == -1 { |
||||
err = e |
||||
} |
||||
return |
||||
} |
||||
|
||||
func Fchdir(fd int) (err error) { |
||||
path, err := Fd2path(fd) |
||||
|
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
return Chdir(path) |
||||
} |
||||
|
||||
type Timespec struct { |
||||
Sec int32 |
||||
Nsec int32 |
||||
} |
||||
|
||||
type Timeval struct { |
||||
Sec int32 |
||||
Usec int32 |
||||
} |
||||
|
||||
func NsecToTimeval(nsec int64) (tv Timeval) { |
||||
nsec += 999 // round up to microsecond
|
||||
tv.Usec = int32(nsec % 1e9 / 1e3) |
||||
tv.Sec = int32(nsec / 1e9) |
||||
return |
||||
} |
||||
|
||||
func nsec() int64 { |
||||
var scratch int64 |
||||
|
||||
r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0) |
||||
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
|
||||
if r0 == 0 { |
||||
return scratch |
||||
} |
||||
return int64(r0) |
||||
} |
||||
|
||||
func Gettimeofday(tv *Timeval) error { |
||||
nsec := nsec() |
||||
*tv = NsecToTimeval(nsec) |
||||
return nil |
||||
} |
||||
|
||||
func Getpagesize() int { return 0x1000 } |
||||
|
||||
func Getegid() (egid int) { return -1 } |
||||
func Geteuid() (euid int) { return -1 } |
||||
func Getgid() (gid int) { return -1 } |
||||
func Getuid() (uid int) { return -1 } |
||||
|
||||
func Getgroups() (gids []int, err error) { |
||||
return make([]int, 0), nil |
||||
} |
||||
|
||||
//sys open(path string, mode int) (fd int, err error)
|
||||
func Open(path string, mode int) (fd int, err error) { |
||||
fixwd() |
||||
return open(path, mode) |
||||
} |
||||
|
||||
//sys create(path string, mode int, perm uint32) (fd int, err error)
|
||||
func Create(path string, mode int, perm uint32) (fd int, err error) { |
||||
fixwd() |
||||
return create(path, mode, perm) |
||||
} |
||||
|
||||
//sys remove(path string) (err error)
|
||||
func Remove(path string) error { |
||||
fixwd() |
||||
return remove(path) |
||||
} |
||||
|
||||
//sys stat(path string, edir []byte) (n int, err error)
|
||||
func Stat(path string, edir []byte) (n int, err error) { |
||||
fixwd() |
||||
return stat(path, edir) |
||||
} |
||||
|
||||
//sys bind(name string, old string, flag int) (err error)
|
||||
func Bind(name string, old string, flag int) (err error) { |
||||
fixwd() |
||||
return bind(name, old, flag) |
||||
} |
||||
|
||||
//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
|
||||
func Mount(fd int, afd int, old string, flag int, aname string) (err error) { |
||||
fixwd() |
||||
return mount(fd, afd, old, flag, aname) |
||||
} |
||||
|
||||
//sys wstat(path string, edir []byte) (err error)
|
||||
func Wstat(path string, edir []byte) (err error) { |
||||
fixwd() |
||||
return wstat(path, edir) |
||||
} |
||||
|
||||
//sys chdir(path string) (err error)
|
||||
//sys Dup(oldfd int, newfd int) (fd int, err error)
|
||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Fstat(fd int, edir []byte) (n int, err error)
|
||||
//sys Fwstat(fd int, edir []byte) (err error)
|
@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,386 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build plan9,386
|
||||
|
||||
package plan9 |
||||
|
||||
import "unsafe" |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(buf) > 0 { |
||||
_p0 = unsafe.Pointer(&buf[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) { |
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(s) > 0 { |
||||
_p0 = unsafe.Pointer(&s[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(aname) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) { |
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) { |
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,amd64 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build plan9,amd64
|
||||
|
||||
package plan9 |
||||
|
||||
import "unsafe" |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(buf) > 0 { |
||||
_p0 = unsafe.Pointer(&buf[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) { |
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(s) > 0 { |
||||
_p0 = unsafe.Pointer(&s[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(aname) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) { |
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) { |
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,arm syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build plan9,arm
|
||||
|
||||
package plan9 |
||||
|
||||
import "unsafe" |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(buf) > 0 { |
||||
_p0 = unsafe.Pointer(&buf[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) { |
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(s) > 0 { |
||||
_p0 = unsafe.Pointer(&s[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(old) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *byte |
||||
_p1, err = BytePtrFromString(aname) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p1 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p1 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) { |
||||
var _p0 *byte |
||||
_p0, err = BytePtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) { |
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) |
||||
fd = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(p) > 0 { |
||||
_p0 = unsafe.Pointer(&p[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) { |
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
n = int(r0) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) { |
||||
var _p0 unsafe.Pointer |
||||
if len(edir) > 0 { |
||||
_p0 = unsafe.Pointer(&edir[0]) |
||||
} else { |
||||
_p0 = unsafe.Pointer(&_zero) |
||||
} |
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) |
||||
if int32(r0) == -1 { |
||||
err = e1 |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,49 @@
|
||||
// mksysnum_plan9.sh /opt/plan9/sys/src/libc/9syscall/sys.h
|
||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
|
||||
|
||||
package plan9 |
||||
|
||||
const ( |
||||
SYS_SYSR1 = 0 |
||||
SYS_BIND = 2 |
||||
SYS_CHDIR = 3 |
||||
SYS_CLOSE = 4 |
||||
SYS_DUP = 5 |
||||
SYS_ALARM = 6 |
||||
SYS_EXEC = 7 |
||||
SYS_EXITS = 8 |
||||
SYS_FAUTH = 10 |
||||
SYS_SEGBRK = 12 |
||||
SYS_OPEN = 14 |
||||
SYS_OSEEK = 16 |
||||
SYS_SLEEP = 17 |
||||
SYS_RFORK = 19 |
||||
SYS_PIPE = 21 |
||||
SYS_CREATE = 22 |
||||
SYS_FD2PATH = 23 |
||||
SYS_BRK_ = 24 |
||||
SYS_REMOVE = 25 |
||||
SYS_NOTIFY = 28 |
||||
SYS_NOTED = 29 |
||||
SYS_SEGATTACH = 30 |
||||
SYS_SEGDETACH = 31 |
||||
SYS_SEGFREE = 32 |
||||
SYS_SEGFLUSH = 33 |
||||
SYS_RENDEZVOUS = 34 |
||||
SYS_UNMOUNT = 35 |
||||
SYS_SEMACQUIRE = 37 |
||||
SYS_SEMRELEASE = 38 |
||||
SYS_SEEK = 39 |
||||
SYS_FVERSION = 40 |
||||
SYS_ERRSTR = 41 |
||||
SYS_STAT = 42 |
||||
SYS_FSTAT = 43 |
||||
SYS_WSTAT = 44 |
||||
SYS_FWSTAT = 45 |
||||
SYS_MOUNT = 46 |
||||
SYS_AWAIT = 47 |
||||
SYS_PREAD = 50 |
||||
SYS_PWRITE = 51 |
||||
SYS_TSEMACQUIRE = 52 |
||||
SYS_NSEC = 53 |
||||
) |
@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes. |
||||
# The master list of authors is in the main Go distribution, |
||||
# visible at http://tip.golang.org/AUTHORS. |
@ -0,0 +1,26 @@
|
||||
# Contributing to Go |
||||
|
||||
Go is an open source project. |
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help! |
||||
|
||||
## Filing issues |
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: |
||||
|
||||
1. What version of Go are you using (`go version`)? |
||||
2. What operating system and processor architecture are you using? |
||||
3. What did you do? |
||||
4. What did you expect to see? |
||||
5. What did you see instead? |
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. |
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug. |
||||
|
||||
## Contributing code |
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) |
||||
before sending patches. |
||||
|
||||
Unless otherwise noted, the Go source files are distributed under |
||||
the BSD-style license found in the LICENSE file. |
@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors. |
||||
# The master list of contributors is in the main Go distribution, |
||||
# visible at http://tip.golang.org/CONTRIBUTORS. |
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following disclaimer |
||||
in the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents) |
||||
|
||||
"This implementation" means the copyrightable works distributed by |
||||
Google as part of the Go project. |
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive, |
||||
no-charge, royalty-free, irrevocable (except as stated in this section) |
||||
patent license to make, have made, use, offer to sell, sell, import, |
||||
transfer and otherwise run, modify and propagate the contents of this |
||||
implementation of Go, where such license applies only to those patent |
||||
claims, both currently owned or controlled by Google and acquired in |
||||
the future, licensable by Google that are necessarily infringed by this |
||||
implementation of Go. This grant does not include claims that would be |
||||
infringed only as a consequence of further modification of this |
||||
implementation. If you or your agent or exclusive licensee institute or |
||||
order or agree to the institution of patent litigation against any |
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging |
||||
that this implementation of Go or any code incorporated within this |
||||
implementation of Go constitutes direct or contributory patent |
||||
infringement, or inducement of patent infringement, then any patent |
||||
rights granted to you under this License for this implementation of Go |
||||
shall terminate as of the date such litigation is filed. |
@ -0,0 +1,17 @@
|
||||
# Go terminal/console support |
||||
|
||||
This repository provides Go terminal and console support packages. |
||||
|
||||
## Download/Install |
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/term`. You can |
||||
also manually git clone the repository to `$GOPATH/src/golang.org/x/term`. |
||||
|
||||
## Report Issues / Send Patches |
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to |
||||
this repository, see https://golang.org/doc/contribute.html. |
||||
|
||||
The main issue tracker for the term repository is located at |
||||
https://github.com/golang/go/issues. Prefix your issue with "x/term:" in the |
||||
subject line, so it is easy to find. |
@ -0,0 +1,5 @@
|
||||
module golang.org/x/term |
||||
|
||||
go 1.11 |
||||
|
||||
require golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 |
@ -0,0 +1,2 @@
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= |
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
@ -0,0 +1,42 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term |
||||
|
||||
import ( |
||||
"fmt" |
||||
"runtime" |
||||
|
||||
"golang.org/x/sys/plan9" |
||||
) |
||||
|
||||
type state struct{} |
||||
|
||||
func isTerminal(fd int) bool { |
||||
path, err := plan9.Fd2path(fd) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
return path == "/dev/cons" || path == "/mnt/term/dev/cons" |
||||
} |
||||
|
||||
func makeRaw(fd int) (*State, error) { |
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func getState(fd int) (*State, error) { |
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func restore(fd int, state *State) error { |
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func getSize(fd int) (width, height int, err error) { |
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func readPassword(fd int) ([]byte, error) { |
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
@ -1,8 +1,8 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package terminal |
||||
package term |
||||
|
||||
import "golang.org/x/sys/unix" |
||||
|
@ -1,10 +1,8 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix
|
||||
|
||||
package terminal |
||||
package term |
||||
|
||||
import "golang.org/x/sys/unix" |
||||
|
@ -0,0 +1,10 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term |
||||
|
||||
import "golang.org/x/sys/unix" |
||||
|
||||
const ioctlReadTermios = unix.TCGETS |
||||
const ioctlWriteTermios = unix.TCSETS |
@ -0,0 +1,38 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9
|
||||
|
||||
package term |
||||
|
||||
import ( |
||||
"fmt" |
||||
"runtime" |
||||
) |
||||
|
||||
type state struct{} |
||||
|
||||
func isTerminal(fd int) bool { |
||||
return false |
||||
} |
||||
|
||||
func makeRaw(fd int) (*State, error) { |
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func getState(fd int) (*State, error) { |
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func restore(fd int, state *State) error { |
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func getSize(fd int) (width, height int, err error) { |
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
||||
|
||||
func readPassword(fd int) ([]byte, error) { |
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) |
||||
} |
@ -0,0 +1,987 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term |
||||
|
||||
import ( |
||||
"bytes" |
||||
"io" |
||||
"runtime" |
||||
"strconv" |
||||
"sync" |
||||
"unicode/utf8" |
||||
) |
||||
|
||||
// EscapeCodes contains escape sequences that can be written to the terminal in
|
||||
// order to achieve different styles of text.
|
||||
type EscapeCodes struct { |
||||
// Foreground colors
|
||||
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte |
||||
|
||||
// Reset all attributes
|
||||
Reset []byte |
||||
} |
||||
|
||||
var vt100EscapeCodes = EscapeCodes{ |
||||
Black: []byte{keyEscape, '[', '3', '0', 'm'}, |
||||
Red: []byte{keyEscape, '[', '3', '1', 'm'}, |
||||
Green: []byte{keyEscape, '[', '3', '2', 'm'}, |
||||
Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, |
||||
Blue: []byte{keyEscape, '[', '3', '4', 'm'}, |
||||
Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, |
||||
Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, |
||||
White: []byte{keyEscape, '[', '3', '7', 'm'}, |
||||
|
||||
Reset: []byte{keyEscape, '[', '0', 'm'}, |
||||
} |
||||
|
||||
// Terminal contains the state for running a VT100 terminal that is capable of
|
||||
// reading lines of input.
|
||||
type Terminal struct { |
||||
// AutoCompleteCallback, if non-null, is called for each keypress with
|
||||
// the full input line and the current position of the cursor (in
|
||||
// bytes, as an index into |line|). If it returns ok=false, the key
|
||||
// press is processed normally. Otherwise it returns a replacement line
|
||||
// and the new cursor position.
|
||||
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) |
||||
|
||||
// Escape contains a pointer to the escape codes for this terminal.
|
||||
// It's always a valid pointer, although the escape codes themselves
|
||||
// may be empty if the terminal doesn't support them.
|
||||
Escape *EscapeCodes |
||||
|
||||
// lock protects the terminal and the state in this object from
|
||||
// concurrent processing of a key press and a Write() call.
|
||||
lock sync.Mutex |
||||
|
||||
c io.ReadWriter |
||||
prompt []rune |
||||
|
||||
// line is the current line being entered.
|
||||
line []rune |
||||
// pos is the logical position of the cursor in line
|
||||
pos int |
||||
// echo is true if local echo is enabled
|
||||
echo bool |
||||
// pasteActive is true iff there is a bracketed paste operation in
|
||||
// progress.
|
||||
pasteActive bool |
||||
|
||||
// cursorX contains the current X value of the cursor where the left
|
||||
// edge is 0. cursorY contains the row number where the first row of
|
||||
// the current line is 0.
|
||||
cursorX, cursorY int |
||||
// maxLine is the greatest value of cursorY so far.
|
||||
maxLine int |
||||
|
||||
termWidth, termHeight int |
||||
|
||||
// outBuf contains the terminal data to be sent.
|
||||
outBuf []byte |
||||
// remainder contains the remainder of any partial key sequences after
|
||||
// a read. It aliases into inBuf.
|
||||
remainder []byte |
||||
inBuf [256]byte |
||||
|
||||
// history contains previously entered commands so that they can be
|
||||
// accessed with the up and down keys.
|
||||
history stRingBuffer |
||||
// historyIndex stores the currently accessed history entry, where zero
|
||||
// means the immediately previous entry.
|
||||
historyIndex int |
||||
// When navigating up and down the history it's possible to return to
|
||||
// the incomplete, initial line. That value is stored in
|
||||
// historyPending.
|
||||
historyPending string |
||||
} |
||||
|
||||
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
|
||||
// a local terminal, that terminal must first have been put into raw mode.
|
||||
// prompt is a string that is written at the start of each input line (i.e.
|
||||
// "> ").
|
||||
func NewTerminal(c io.ReadWriter, prompt string) *Terminal { |
||||
return &Terminal{ |
||||
Escape: &vt100EscapeCodes, |
||||
c: c, |
||||
prompt: []rune(prompt), |
||||
termWidth: 80, |
||||
termHeight: 24, |
||||
echo: true, |
||||
historyIndex: -1, |
||||
} |
||||
} |
||||
|
||||
const ( |
||||
keyCtrlC = 3 |
||||
keyCtrlD = 4 |
||||
keyCtrlU = 21 |
||||
keyEnter = '\r' |
||||
keyEscape = 27 |
||||
keyBackspace = 127 |
||||
keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota |
||||
keyUp |
||||
keyDown |
||||
keyLeft |
||||
keyRight |
||||
keyAltLeft |
||||
keyAltRight |
||||
keyHome |
||||
keyEnd |
||||
keyDeleteWord |
||||
keyDeleteLine |
||||
keyClearScreen |
||||
keyPasteStart |
||||
keyPasteEnd |
||||
) |
||||
|
||||
var ( |
||||
crlf = []byte{'\r', '\n'} |
||||
pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} |
||||
pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} |
||||
) |
||||
|
||||
// bytesToKey tries to parse a key sequence from b. If successful, it returns
|
||||
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
|
||||
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { |
||||
if len(b) == 0 { |
||||
return utf8.RuneError, nil |
||||
} |
||||
|
||||
if !pasteActive { |
||||
switch b[0] { |
||||
case 1: // ^A
|
||||
return keyHome, b[1:] |
||||
case 2: // ^B
|
||||
return keyLeft, b[1:] |
||||
case 5: // ^E
|
||||
return keyEnd, b[1:] |
||||
case 6: // ^F
|
||||
return keyRight, b[1:] |
||||
case 8: // ^H
|
||||
return keyBackspace, b[1:] |
||||
case 11: // ^K
|
||||
return keyDeleteLine, b[1:] |
||||
case 12: // ^L
|
||||
return keyClearScreen, b[1:] |
||||
case 23: // ^W
|
||||
return keyDeleteWord, b[1:] |
||||
case 14: // ^N
|
||||
return keyDown, b[1:] |
||||
case 16: // ^P
|
||||
return keyUp, b[1:] |
||||
} |
||||
} |
||||
|
||||
if b[0] != keyEscape { |
||||
if !utf8.FullRune(b) { |
||||
return utf8.RuneError, b |
||||
} |
||||
r, l := utf8.DecodeRune(b) |
||||
return r, b[l:] |
||||
} |
||||
|
||||
if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { |
||||
switch b[2] { |
||||
case 'A': |
||||
return keyUp, b[3:] |
||||
case 'B': |
||||
return keyDown, b[3:] |
||||
case 'C': |
||||
return keyRight, b[3:] |
||||
case 'D': |
||||
return keyLeft, b[3:] |
||||
case 'H': |
||||
return keyHome, b[3:] |
||||
case 'F': |
||||
return keyEnd, b[3:] |
||||
} |
||||
} |
||||
|
||||
if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { |
||||
switch b[5] { |
||||
case 'C': |
||||
return keyAltRight, b[6:] |
||||
case 'D': |
||||
return keyAltLeft, b[6:] |
||||
} |
||||
} |
||||
|
||||
if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { |
||||
return keyPasteStart, b[6:] |
||||
} |
||||
|
||||
if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { |
||||
return keyPasteEnd, b[6:] |
||||
} |
||||
|
||||
// If we get here then we have a key that we don't recognise, or a
|
||||
// partial sequence. It's not clear how one should find the end of a
|
||||
// sequence without knowing them all, but it seems that [a-zA-Z~] only
|
||||
// appears at the end of a sequence.
|
||||
for i, c := range b[0:] { |
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { |
||||
return keyUnknown, b[i+1:] |
||||
} |
||||
} |
||||
|
||||
return utf8.RuneError, b |
||||
} |
||||
|
||||
// queue appends data to the end of t.outBuf
|
||||
func (t *Terminal) queue(data []rune) { |
||||
t.outBuf = append(t.outBuf, []byte(string(data))...) |
||||
} |
||||
|
||||
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} |
||||
var space = []rune{' '} |
||||
|
||||
func isPrintable(key rune) bool { |
||||
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff |
||||
return key >= 32 && !isInSurrogateArea |
||||
} |
||||
|
||||
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
|
||||
// given, logical position in the text.
|
||||
func (t *Terminal) moveCursorToPos(pos int) { |
||||
if !t.echo { |
||||
return |
||||
} |
||||
|
||||
x := visualLength(t.prompt) + pos |
||||
y := x / t.termWidth |
||||
x = x % t.termWidth |
||||
|
||||
up := 0 |
||||
if y < t.cursorY { |
||||
up = t.cursorY - y |
||||
} |
||||
|
||||
down := 0 |
||||
if y > t.cursorY { |
||||
down = y - t.cursorY |
||||
} |
||||
|
||||
left := 0 |
||||
if x < t.cursorX { |
||||
left = t.cursorX - x |
||||
} |
||||
|
||||
right := 0 |
||||
if x > t.cursorX { |
||||
right = x - t.cursorX |
||||
} |
||||
|
||||
t.cursorX = x |
||||
t.cursorY = y |
||||
t.move(up, down, left, right) |
||||
} |
||||
|
||||
func (t *Terminal) move(up, down, left, right int) { |
||||
m := []rune{} |
||||
|
||||
// 1 unit up can be expressed as ^[[A or ^[A
|
||||
// 5 units up can be expressed as ^[[5A
|
||||
|
||||
if up == 1 { |
||||
m = append(m, keyEscape, '[', 'A') |
||||
} else if up > 1 { |
||||
m = append(m, keyEscape, '[') |
||||
m = append(m, []rune(strconv.Itoa(up))...) |
||||
m = append(m, 'A') |
||||
} |
||||
|
||||
if down == 1 { |
||||
m = append(m, keyEscape, '[', 'B') |
||||
} else if down > 1 { |
||||
m = append(m, keyEscape, '[') |
||||
m = append(m, []rune(strconv.Itoa(down))...) |
||||
m = append(m, 'B') |
||||
} |
||||
|
||||
if right == 1 { |
||||
m = append(m, keyEscape, '[', 'C') |
||||
} else if right > 1 { |
||||
m = append(m, keyEscape, '[') |
||||
m = append(m, []rune(strconv.Itoa(right))...) |
||||
m = append(m, 'C') |
||||
} |
||||
|
||||
if left == 1 { |
||||
m = append(m, keyEscape, '[', 'D') |
||||
} else if left > 1 { |
||||
m = append(m, keyEscape, '[') |
||||
m = append(m, []rune(strconv.Itoa(left))...) |
||||
m = append(m, 'D') |
||||
} |
||||
|
||||
t.queue(m) |
||||
} |
||||
|
||||
func (t *Terminal) clearLineToRight() { |
||||
op := []rune{keyEscape, '[', 'K'} |
||||
t.queue(op) |
||||
} |
||||
|
||||
const maxLineLength = 4096 |
||||
|
||||
func (t *Terminal) setLine(newLine []rune, newPos int) { |
||||
if t.echo { |
||||
t.moveCursorToPos(0) |
||||
t.writeLine(newLine) |
||||
for i := len(newLine); i < len(t.line); i++ { |
||||
t.writeLine(space) |
||||
} |
||||
t.moveCursorToPos(newPos) |
||||
} |
||||
t.line = newLine |
||||
t.pos = newPos |
||||
} |
||||
|
||||
func (t *Terminal) advanceCursor(places int) { |
||||
t.cursorX += places |
||||
t.cursorY += t.cursorX / t.termWidth |
||||
if t.cursorY > t.maxLine { |
||||
t.maxLine = t.cursorY |
||||
} |
||||
t.cursorX = t.cursorX % t.termWidth |
||||
|
||||
if places > 0 && t.cursorX == 0 { |
||||
// Normally terminals will advance the current position
|
||||
// when writing a character. But that doesn't happen
|
||||
// for the last character in a line. However, when
|
||||
// writing a character (except a new line) that causes
|
||||
// a line wrap, the position will be advanced two
|
||||
// places.
|
||||
//
|
||||
// So, if we are stopping at the end of a line, we
|
||||
// need to write a newline so that our cursor can be
|
||||
// advanced to the next line.
|
||||
t.outBuf = append(t.outBuf, '\r', '\n') |
||||
} |
||||
} |
||||
|
||||
func (t *Terminal) eraseNPreviousChars(n int) { |
||||
if n == 0 { |
||||
return |
||||
} |
||||
|
||||
if t.pos < n { |
||||
n = t.pos |
||||
} |
||||
t.pos -= n |
||||
t.moveCursorToPos(t.pos) |
||||
|
||||
copy(t.line[t.pos:], t.line[n+t.pos:]) |
||||
t.line = t.line[:len(t.line)-n] |
||||
if t.echo { |
||||
t.writeLine(t.line[t.pos:]) |
||||
for i := 0; i < n; i++ { |
||||
t.queue(space) |
||||
} |
||||
t.advanceCursor(n) |
||||
t.moveCursorToPos(t.pos) |
||||
} |
||||
} |
||||
|
||||
// countToLeftWord returns then number of characters from the cursor to the
|
||||
// start of the previous word.
|
||||
func (t *Terminal) countToLeftWord() int { |
||||
if t.pos == 0 { |
||||
return 0 |
||||
} |
||||
|
||||
pos := t.pos - 1 |
||||
for pos > 0 { |
||||
if t.line[pos] != ' ' { |
||||
break |
||||
} |
||||
pos-- |
||||
} |
||||
for pos > 0 { |
||||
if t.line[pos] == ' ' { |
||||
pos++ |
||||
break |
||||
} |
||||
pos-- |
||||
} |
||||
|
||||
return t.pos - pos |
||||
} |
||||
|
||||
// countToRightWord returns then number of characters from the cursor to the
|
||||
// start of the next word.
|
||||
func (t *Terminal) countToRightWord() int { |
||||
pos := t.pos |
||||
for pos < len(t.line) { |
||||
if t.line[pos] == ' ' { |
||||
break |
||||
} |
||||
pos++ |
||||
} |
||||
for pos < len(t.line) { |
||||
if t.line[pos] != ' ' { |
||||
break |
||||
} |
||||
pos++ |
||||
} |
||||
return pos - t.pos |
||||
} |
||||
|
||||
// visualLength returns the number of visible glyphs in s.
|
||||
func visualLength(runes []rune) int { |
||||
inEscapeSeq := false |
||||
length := 0 |
||||
|
||||
for _, r := range runes { |
||||
switch { |
||||
case inEscapeSeq: |
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { |
||||
inEscapeSeq = false |
||||
} |
||||
case r == '\x1b': |
||||
inEscapeSeq = true |
||||
default: |
||||
length++ |
||||
} |
||||
} |
||||
|
||||
return length |
||||
} |
||||
|
||||
// handleKey processes the given key and, optionally, returns a line of text
|
||||
// that the user has entered.
|
||||
func (t *Terminal) handleKey(key rune) (line string, ok bool) { |
||||
if t.pasteActive && key != keyEnter { |
||||
t.addKeyToLine(key) |
||||
return |
||||
} |
||||
|
||||
switch key { |
||||
case keyBackspace: |
||||
if t.pos == 0 { |
||||
return |
||||
} |
||||
t.eraseNPreviousChars(1) |
||||
case keyAltLeft: |
||||
// move left by a word.
|
||||
t.pos -= t.countToLeftWord() |
||||
t.moveCursorToPos(t.pos) |
||||
case keyAltRight: |
||||
// move right by a word.
|
||||
t.pos += t.countToRightWord() |
||||
t.moveCursorToPos(t.pos) |
||||
case keyLeft: |
||||
if t.pos == 0 { |
||||
return |
||||
} |
||||
t.pos-- |
||||
t.moveCursorToPos(t.pos) |
||||
case keyRight: |
||||
if t.pos == len(t.line) { |
||||
return |
||||
} |
||||
t.pos++ |
||||
t.moveCursorToPos(t.pos) |
||||
case keyHome: |
||||
if t.pos == 0 { |
||||
return |
||||
} |
||||
t.pos = 0 |
||||
t.moveCursorToPos(t.pos) |
||||
case keyEnd: |
||||
if t.pos == len(t.line) { |
||||
return |
||||
} |
||||
t.pos = len(t.line) |
||||
t.moveCursorToPos(t.pos) |
||||
case keyUp: |
||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) |
||||
if !ok { |
||||
return "", false |
||||
} |
||||
if t.historyIndex == -1 { |
||||
t.historyPending = string(t.line) |
||||
} |
||||
t.historyIndex++ |
||||
runes := []rune(entry) |
||||
t.setLine(runes, len(runes)) |
||||
case keyDown: |
||||
switch t.historyIndex { |
||||
case -1: |
||||
return |
||||
case 0: |
||||
runes := []rune(t.historyPending) |
||||
t.setLine(runes, len(runes)) |
||||
t.historyIndex-- |
||||
default: |
||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) |
||||
if ok { |
||||
t.historyIndex-- |
||||
runes := []rune(entry) |
||||
t.setLine(runes, len(runes)) |
||||
} |
||||
} |
||||
case keyEnter: |
||||
t.moveCursorToPos(len(t.line)) |
||||
t.queue([]rune("\r\n")) |
||||
line = string(t.line) |
||||
ok = true |
||||
t.line = t.line[:0] |
||||
t.pos = 0 |
||||
t.cursorX = 0 |
||||
t.cursorY = 0 |
||||
t.maxLine = 0 |
||||
case keyDeleteWord: |
||||
// Delete zero or more spaces and then one or more characters.
|
||||
t.eraseNPreviousChars(t.countToLeftWord()) |
||||
case keyDeleteLine: |
||||
// Delete everything from the current cursor position to the
|
||||
// end of line.
|
||||
for i := t.pos; i < len(t.line); i++ { |
||||
t.queue(space) |
||||
t.advanceCursor(1) |
||||
} |
||||
t.line = t.line[:t.pos] |
||||
t.moveCursorToPos(t.pos) |
||||
case keyCtrlD: |
||||
// Erase the character under the current position.
|
||||
// The EOF case when the line is empty is handled in
|
||||
// readLine().
|
||||
if t.pos < len(t.line) { |
||||
t.pos++ |
||||
t.eraseNPreviousChars(1) |
||||
} |
||||
case keyCtrlU: |
||||
t.eraseNPreviousChars(t.pos) |
||||
case keyClearScreen: |
||||
// Erases the screen and moves the cursor to the home position.
|
||||
t.queue([]rune("\x1b[2J\x1b[H")) |
||||
t.queue(t.prompt) |
||||
t.cursorX, t.cursorY = 0, 0 |
||||
t.advanceCursor(visualLength(t.prompt)) |
||||
t.setLine(t.line, t.pos) |
||||
default: |
||||
if t.AutoCompleteCallback != nil { |
||||
prefix := string(t.line[:t.pos]) |
||||
suffix := string(t.line[t.pos:]) |
||||
|
||||
t.lock.Unlock() |
||||
newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) |
||||
t.lock.Lock() |
||||
|
||||
if completeOk { |
||||
t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) |
||||
return |
||||
} |
||||
} |
||||
if !isPrintable(key) { |
||||
return |
||||
} |
||||
if len(t.line) == maxLineLength { |
||||
return |
||||
} |
||||
t.addKeyToLine(key) |
||||
} |
||||
return |
||||
} |
||||
|
||||
// addKeyToLine inserts the given key at the current position in the current
|
||||
// line.
|
||||
func (t *Terminal) addKeyToLine(key rune) { |
||||
if len(t.line) == cap(t.line) { |
||||
newLine := make([]rune, len(t.line), 2*(1+len(t.line))) |
||||
copy(newLine, t.line) |
||||
t.line = newLine |
||||
} |
||||
t.line = t.line[:len(t.line)+1] |
||||
copy(t.line[t.pos+1:], t.line[t.pos:]) |
||||
t.line[t.pos] = key |
||||
if t.echo { |
||||
t.writeLine(t.line[t.pos:]) |
||||
} |
||||
t.pos++ |
||||
t.moveCursorToPos(t.pos) |
||||
} |
||||
|
||||
func (t *Terminal) writeLine(line []rune) { |
||||
for len(line) != 0 { |
||||
remainingOnLine := t.termWidth - t.cursorX |
||||
todo := len(line) |
||||
if todo > remainingOnLine { |
||||
todo = remainingOnLine |
||||
} |
||||
t.queue(line[:todo]) |
||||
t.advanceCursor(visualLength(line[:todo])) |
||||
line = line[todo:] |
||||
} |
||||
} |
||||
|
||||
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
|
||||
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { |
||||
for len(buf) > 0 { |
||||
i := bytes.IndexByte(buf, '\n') |
||||
todo := len(buf) |
||||
if i >= 0 { |
||||
todo = i |
||||
} |
||||
|
||||
var nn int |
||||
nn, err = w.Write(buf[:todo]) |
||||
n += nn |
||||
if err != nil { |
||||
return n, err |
||||
} |
||||
buf = buf[todo:] |
||||
|
||||
if i >= 0 { |
||||
if _, err = w.Write(crlf); err != nil { |
||||
return n, err |
||||
} |
||||
n++ |
||||
buf = buf[1:] |
||||
} |
||||
} |
||||
|
||||
return n, nil |
||||
} |
||||
|
||||
func (t *Terminal) Write(buf []byte) (n int, err error) { |
||||
t.lock.Lock() |
||||
defer t.lock.Unlock() |
||||
|
||||
if t.cursorX == 0 && t.cursorY == 0 { |
||||
// This is the easy case: there's nothing on the screen that we
|
||||
// have to move out of the way.
|
||||
return writeWithCRLF(t.c, buf) |
||||
} |
||||
|
||||
// We have a prompt and possibly user input on the screen. We
|
||||
// have to clear it first.
|
||||
t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) |
||||
t.cursorX = 0 |
||||
t.clearLineToRight() |
||||
|
||||
for t.cursorY > 0 { |
||||
t.move(1 /* up */, 0, 0, 0) |
||||
t.cursorY-- |
||||
t.clearLineToRight() |
||||
} |
||||
|
||||
if _, err = t.c.Write(t.outBuf); err != nil { |
||||
return |
||||
} |
||||
t.outBuf = t.outBuf[:0] |
||||
|
||||
if n, err = writeWithCRLF(t.c, buf); err != nil { |
||||
return |
||||
} |
||||
|
||||
t.writeLine(t.prompt) |
||||
if t.echo { |
||||
t.writeLine(t.line) |
||||
} |
||||
|
||||
t.moveCursorToPos(t.pos) |
||||
|
||||
if _, err = t.c.Write(t.outBuf); err != nil { |
||||
return |
||||
} |
||||
t.outBuf = t.outBuf[:0] |
||||
return |
||||
} |
||||
|
||||
// ReadPassword temporarily changes the prompt and reads a password, without
|
||||
// echo, from the terminal.
|
||||
func (t *Terminal) ReadPassword(prompt string) (line string, err error) { |
||||
t.lock.Lock() |
||||
defer t.lock.Unlock() |
||||
|
||||
oldPrompt := t.prompt |
||||
t.prompt = []rune(prompt) |
||||
t.echo = false |
||||
|
||||
line, err = t.readLine() |
||||
|
||||
t.prompt = oldPrompt |
||||
t.echo = true |
||||
|
||||
return |
||||
} |
||||
|
||||
// ReadLine returns a line of input from the terminal.
|
||||
func (t *Terminal) ReadLine() (line string, err error) { |
||||
t.lock.Lock() |
||||
defer t.lock.Unlock() |
||||
|
||||
return t.readLine() |
||||
} |
||||
|
||||
func (t *Terminal) readLine() (line string, err error) { |
||||
// t.lock must be held at this point
|
||||
|
||||
if t.cursorX == 0 && t.cursorY == 0 { |
||||
t.writeLine(t.prompt) |
||||
t.c.Write(t.outBuf) |
||||
t.outBuf = t.outBuf[:0] |
||||
} |
||||
|
||||
lineIsPasted := t.pasteActive |
||||
|
||||
for { |
||||
rest := t.remainder |
||||
lineOk := false |
||||
for !lineOk { |
||||
var key rune |
||||
key, rest = bytesToKey(rest, t.pasteActive) |
||||
if key == utf8.RuneError { |
||||
break |
||||
} |
||||
if !t.pasteActive { |
||||
if key == keyCtrlD { |
||||
if len(t.line) == 0 { |
||||
return "", io.EOF |
||||
} |
||||
} |
||||
if key == keyCtrlC { |
||||
return "", io.EOF |
||||
} |
||||
if key == keyPasteStart { |
||||
t.pasteActive = true |
||||
if len(t.line) == 0 { |
||||
lineIsPasted = true |
||||
} |
||||
continue |
||||
} |
||||
} else if key == keyPasteEnd { |
||||
t.pasteActive = false |
||||
continue |
||||
} |
||||
if !t.pasteActive { |
||||
lineIsPasted = false |
||||
} |
||||
line, lineOk = t.handleKey(key) |
||||
} |
||||
if len(rest) > 0 { |
||||
n := copy(t.inBuf[:], rest) |
||||
t.remainder = t.inBuf[:n] |
||||
} else { |
||||
t.remainder = nil |
||||
} |
||||
t.c.Write(t.outBuf) |
||||
t.outBuf = t.outBuf[:0] |
||||
if lineOk { |
||||
if t.echo { |
||||
t.historyIndex = -1 |
||||
t.history.Add(line) |
||||
} |
||||
if lineIsPasted { |
||||
err = ErrPasteIndicator |
||||
} |
||||
return |
||||
} |
||||
|
||||
// t.remainder is a slice at the beginning of t.inBuf
|
||||
// containing a partial key sequence
|
||||
readBuf := t.inBuf[len(t.remainder):] |
||||
var n int |
||||
|
||||
t.lock.Unlock() |
||||
n, err = t.c.Read(readBuf) |
||||
t.lock.Lock() |
||||
|
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
t.remainder = t.inBuf[:n+len(t.remainder)] |
||||
} |
||||
} |
||||
|
||||
// SetPrompt sets the prompt to be used when reading subsequent lines.
|
||||
func (t *Terminal) SetPrompt(prompt string) { |
||||
t.lock.Lock() |
||||
defer t.lock.Unlock() |
||||
|
||||
t.prompt = []rune(prompt) |
||||
} |
||||
|
||||
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { |
||||
// Move cursor to column zero at the start of the line.
|
||||
t.move(t.cursorY, 0, t.cursorX, 0) |
||||
t.cursorX, t.cursorY = 0, 0 |
||||
t.clearLineToRight() |
||||
for t.cursorY < numPrevLines { |
||||
// Move down a line
|
||||
t.move(0, 1, 0, 0) |
||||
t.cursorY++ |
||||
t.clearLineToRight() |
||||
} |
||||
// Move back to beginning.
|
||||
t.move(t.cursorY, 0, 0, 0) |
||||
t.cursorX, t.cursorY = 0, 0 |
||||
|
||||
t.queue(t.prompt) |
||||
t.advanceCursor(visualLength(t.prompt)) |
||||
t.writeLine(t.line) |
||||
t.moveCursorToPos(t.pos) |
||||
} |
||||
|
||||
func (t *Terminal) SetSize(width, height int) error { |
||||
t.lock.Lock() |
||||
defer t.lock.Unlock() |
||||
|
||||
if width == 0 { |
||||
width = 1 |
||||
} |
||||
|
||||
oldWidth := t.termWidth |
||||
t.termWidth, t.termHeight = width, height |
||||
|
||||
switch { |
||||
case width == oldWidth: |
||||
// If the width didn't change then nothing else needs to be
|
||||
// done.
|
||||
return nil |
||||
case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: |
||||
// If there is nothing on current line and no prompt printed,
|
||||
// just do nothing
|
||||
return nil |
||||
case width < oldWidth: |
||||
// Some terminals (e.g. xterm) will truncate lines that were
|
||||
// too long when shinking. Others, (e.g. gnome-terminal) will
|
||||
// attempt to wrap them. For the former, repainting t.maxLine
|
||||
// works great, but that behaviour goes badly wrong in the case
|
||||
// of the latter because they have doubled every full line.
|
||||
|
||||
// We assume that we are working on a terminal that wraps lines
|
||||
// and adjust the cursor position based on every previous line
|
||||
// wrapping and turning into two. This causes the prompt on
|
||||
// xterms to move upwards, which isn't great, but it avoids a
|
||||
// huge mess with gnome-terminal.
|
||||
if t.cursorX >= t.termWidth { |
||||
t.cursorX = t.termWidth - 1 |
||||
} |
||||
t.cursorY *= 2 |
||||
t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) |
||||
case width > oldWidth: |
||||
// If the terminal expands then our position calculations will
|
||||
// be wrong in the future because we think the cursor is
|
||||
// |t.pos| chars into the string, but there will be a gap at
|
||||
// the end of any wrapped line.
|
||||
//
|
||||
// But the position will actually be correct until we move, so
|
||||
// we can move back to the beginning and repaint everything.
|
||||
t.clearAndRepaintLinePlusNPrevious(t.maxLine) |
||||
} |
||||
|
||||
_, err := t.c.Write(t.outBuf) |
||||
t.outBuf = t.outBuf[:0] |
||||
return err |
||||
} |
||||
|
||||
type pasteIndicatorError struct{} |
||||
|
||||
func (pasteIndicatorError) Error() string { |
||||
return "terminal: ErrPasteIndicator not correctly handled" |
||||
} |
||||
|
||||
// ErrPasteIndicator may be returned from ReadLine as the error, in addition
|
||||
// to valid line data. It indicates that bracketed paste mode is enabled and
|
||||
// that the returned line consists only of pasted data. Programs may wish to
|
||||
// interpret pasted data more literally than typed data.
|
||||
var ErrPasteIndicator = pasteIndicatorError{} |
||||
|
||||
// SetBracketedPasteMode requests that the terminal bracket paste operations
|
||||
// with markers. Not all terminals support this but, if it is supported, then
|
||||
// enabling this mode will stop any autocomplete callback from running due to
|
||||
// pastes. Additionally, any lines that are completely pasted will be returned
|
||||
// from ReadLine with the error set to ErrPasteIndicator.
|
||||
func (t *Terminal) SetBracketedPasteMode(on bool) { |
||||
if on { |
||||
io.WriteString(t.c, "\x1b[?2004h") |
||||
} else { |
||||
io.WriteString(t.c, "\x1b[?2004l") |
||||
} |
||||
} |
||||
|
||||
// stRingBuffer is a ring buffer of strings.
|
||||
type stRingBuffer struct { |
||||
// entries contains max elements.
|
||||
entries []string |
||||
max int |
||||
// head contains the index of the element most recently added to the ring.
|
||||
head int |
||||
// size contains the number of elements in the ring.
|
||||
size int |
||||
} |
||||
|
||||
func (s *stRingBuffer) Add(a string) { |
||||
if s.entries == nil { |
||||
const defaultNumEntries = 100 |
||||
s.entries = make([]string, defaultNumEntries) |
||||
s.max = defaultNumEntries |
||||
} |
||||
|
||||
s.head = (s.head + 1) % s.max |
||||
s.entries[s.head] = a |
||||
if s.size < s.max { |
||||
s.size++ |
||||
} |
||||
} |
||||
|
||||
// NthPreviousEntry returns the value passed to the nth previous call to Add.
|
||||
// If n is zero then the immediately prior value is returned, if one, then the
|
||||
// next most recent, and so on. If such an element doesn't exist then ok is
|
||||
// false.
|
||||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { |
||||
if n >= s.size { |
||||
return "", false |
||||
} |
||||
index := s.head - n |
||||
if index < 0 { |
||||
index += s.max |
||||
} |
||||
return s.entries[index], true |
||||
} |
||||
|
||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
||||
// The slice returned does not include the \n.
|
||||
// readPasswordLine also ignores any \r it finds.
|
||||
// Windows uses \r as end of line. So, on Windows, readPasswordLine
|
||||
// reads until it finds \r and ignores any \n it finds during processing.
|
||||
func readPasswordLine(reader io.Reader) ([]byte, error) { |
||||
var buf [1]byte |
||||
var ret []byte |
||||
|
||||
for { |
||||
n, err := reader.Read(buf[:]) |
||||
if n > 0 { |
||||
switch buf[0] { |
||||
case '\b': |
||||
if len(ret) > 0 { |
||||
ret = ret[:len(ret)-1] |
||||
} |
||||
case '\n': |
||||
if runtime.GOOS != "windows" { |
||||
return ret, nil |
||||
} |
||||
// otherwise ignore \n
|
||||
case '\r': |
||||
if runtime.GOOS == "windows" { |
||||
return ret, nil |
||||
} |
||||
// otherwise ignore \r
|
||||
default: |
||||
ret = append(ret, buf[0]) |
||||
} |
||||
continue |
||||
} |
||||
if err != nil { |
||||
if err == io.EOF && len(ret) > 0 { |
||||
return ret, nil |
||||
} |
||||
return ret, err |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue