github.com/yandex-cloud/geesefs@v0.40.9/internal/cfg/flags.go (about)

     1  // Copyright 2015 - 2017 Ka-Hing Cheung
     2  // Copyright 2015 - 2017 Google Inc. All Rights Reserved.
     3  // Copyright 2021 Yandex LLC
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package cfg
    18  
    19  import (
    20  	"io"
    21  	"os"
    22  	"runtime"
    23  	"strconv"
    24  	"strings"
    25  	"syscall"
    26  	"text/tabwriter"
    27  	"text/template"
    28  	"time"
    29  
    30  	"github.com/urfave/cli"
    31  )
    32  
    33  const GEESEFS_VERSION = "0.40.9"
    34  
    35  var flagCategories map[string]string
    36  
    37  // Set up custom help text for goofys; in particular the usage section.
    38  func filterCategory(flags []cli.Flag, category string) (ret []cli.Flag) {
    39  	for _, f := range flags {
    40  		if flagCategories[f.GetName()] == category {
    41  			ret = append(ret, f)
    42  		}
    43  	}
    44  	return
    45  }
    46  
    47  var VersionHash string
    48  var FuseOptions string
    49  
    50  func NewApp() (app *cli.App) {
    51  	cli.AppHelpTemplate = `NAME:
    52     {{.Name}} - {{.Usage}}
    53  
    54  USAGE:
    55     {{.Name}} {{if .Flags}}[global options]{{end}} bucket[:prefix] mountpoint
    56     {{if .Version}}
    57  VERSION:
    58     {{.Version}}
    59     {{end}}{{if len .Authors}}
    60  AUTHOR(S):
    61     {{range .Authors}}{{ . }}{{end}}
    62     {{end}}{{if .Commands}}
    63  COMMANDS:
    64     {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
    65     {{end}}{{end}}{{if .Flags}}
    66  GLOBAL OPTIONS:
    67     {{range category .Flags ""}}{{.}}
    68     {{end}}
    69  TUNING OPTIONS:
    70     {{range category .Flags "tuning"}}{{.}}
    71     {{end}}
    72  S3 OPTIONS:
    73     {{range category .Flags "aws"}}{{.}}
    74     {{end}}
    75  MISC OPTIONS:
    76     {{range category .Flags "misc"}}{{.}}
    77     {{end}}{{end}}
    78  `+FuseOptions+`{{if .Copyright }}COPYRIGHT:
    79     {{.Copyright}}
    80     {{end}}
    81  `
    82  
    83  	uid, gid := MyUserAndGroup()
    84  
    85  	s3Default := (&S3Config{}).Init()
    86  
    87  	fsFlags := []cli.Flag{
    88  		/////////////////////////
    89  		// File system
    90  		/////////////////////////
    91  
    92  		cli.StringSliceFlag{
    93  			Name:  "o",
    94  			Usage: "Additional system-specific mount options. Be careful!",
    95  		},
    96  
    97  		cli.StringFlag{
    98  			Name: "cache",
    99  			Usage: "Directory to use for data cache. (default: off)",
   100  		},
   101  
   102  		cli.IntFlag{
   103  			Name:  "dir-mode",
   104  			Value: 0755,
   105  			Usage: "Permission bits for directories. (default: 0755)",
   106  		},
   107  
   108  		cli.IntFlag{
   109  			Name:  "file-mode",
   110  			Value: 0644,
   111  			Usage: "Permission bits for files. (default: 0644)",
   112  		},
   113  
   114  		cli.IntFlag{
   115  			Name:  "cache-file-mode",
   116  			Value: 0644,
   117  			Usage: "Permission bits for disk cache files. (default: 0644)",
   118  		},
   119  
   120  		cli.IntFlag{
   121  			Name:  "uid",
   122  			Value: uid,
   123  			Usage: "UID owner of all inodes.",
   124  		},
   125  
   126  		cli.IntFlag{
   127  			Name:  "gid",
   128  			Value: gid,
   129  			Usage: "GID owner of all inodes.",
   130  		},
   131  
   132  		cli.IntFlag{
   133  			Name:  "setuid",
   134  			Value: uid,
   135  			Usage: "Drop root privileges and change to this user ID (defaults to --uid).",
   136  		},
   137  
   138  		cli.IntFlag{
   139  			Name:  "setgid",
   140  			Value: gid,
   141  			Usage: "Drop root group and change to this group ID (defaults to --gid).",
   142  		},
   143  	}
   144  
   145  	s3Flags := []cli.Flag{
   146  		/////////////////////////
   147  		// S3
   148  		/////////////////////////
   149  
   150  		cli.StringFlag{
   151  			Name:  "endpoint",
   152  			Value: "https://storage.yandexcloud.net",
   153  			Usage: "The S3 endpoint to connect to." +
   154  				" Possible values: http://127.0.0.1:8081/, https://s3.amazonaws.com",
   155  		},
   156  
   157  		cli.StringFlag{
   158  			Name:  "project-id",
   159  			Value: "",
   160  			Usage: "Project ID for Ceph multi-tenancy bucket sharing (bucket syntax project-id:bucket-name)",
   161  		},
   162  
   163  		cli.BoolFlag{
   164  			Name:  "iam",
   165  			Usage: "Try to authenticate automatically using VM metadata service (Yandex Cloud / IMDSv1 / GCP)",
   166  		},
   167  
   168  		cli.StringFlag{
   169  			Name:  "iam-header",
   170  			Value: "X-YaCloud-SubjectToken",
   171  			Usage: "The header to use for authenticating with IAM token",
   172  		},
   173  
   174  		cli.StringFlag{
   175  			Name:  "iam-flavor",
   176  			Value: "gcp",
   177  			Usage: "Instance metadata service flavor: gcp or imdsv1",
   178  		},
   179  
   180  		cli.StringFlag{
   181  			Name:  "iam-url",
   182  			Value: "",
   183  			Usage: "Custom instance metadata service URL",
   184  		},
   185  
   186  		cli.StringFlag{
   187  			Name:  "region",
   188  			Value: s3Default.Region,
   189  			Usage: "The region to connect to. Usually this is auto-detected." +
   190  				" Possible values: us-east-1, us-west-1, us-west-2, eu-west-1, " +
   191  				"eu-central-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, " +
   192  				"sa-east-1, cn-north-1",
   193  		},
   194  
   195  		cli.BoolFlag{
   196  			Name:  "requester-pays",
   197  			Usage: "Whether to allow access to requester-pays buckets (default: off)",
   198  		},
   199  
   200  		cli.StringFlag{
   201  			Name:  "storage-class",
   202  			Value: s3Default.StorageClass,
   203  			Usage: "The type of storage to use when writing objects." +
   204  				" Possible values: REDUCED_REDUNDANCY, STANDARD, STANDARD_IA.",
   205  		},
   206  
   207  		cli.Uint64Flag{
   208  			Name:  "cold-min-size",
   209  			Usage: "Objects smaller than this size will be stored in STANDARD if STANDARD_IA (cold storage) is selected as default.",
   210  		},
   211  
   212  		cli.StringFlag{
   213  			Name:  "profile",
   214  			Usage: "Use a named profile from $HOME/.aws/credentials instead of \"default\"",
   215  		},
   216  
   217  		cli.StringSliceFlag{
   218  			Name:  "shared-config",
   219  			Usage: "Use different shared configuration file(s) instead of $HOME/.aws/credentials and $HOME/.aws/config",
   220  		},
   221  
   222  		cli.BoolFlag{
   223  			Name:  "use-content-type",
   224  			Usage: "Set Content-Type according to file extension and /etc/mime.types (default: off)",
   225  		},
   226  
   227  		/// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
   228  		/// See http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html
   229  		cli.BoolFlag{
   230  			Name:  "sse",
   231  			Usage: "Enable basic server-side encryption at rest (SSE-S3) in S3 for all writes (default: off)",
   232  		},
   233  
   234  		cli.StringFlag{
   235  			Name:  "sse-kms",
   236  			Usage: "Enable KMS encryption (SSE-KMS) for all writes using this particular KMS `key-id`. Leave blank to Use the account's CMK - customer master key (default: off)",
   237  			Value: "",
   238  		},
   239  
   240  		cli.StringFlag{
   241  			Name:  "sse-c",
   242  			Usage: "Enable server-side encryption using this base64-encoded key (default: off)",
   243  			Value: "",
   244  		},
   245  
   246  		cli.BoolFlag{
   247  			Name:  "no-checksum",
   248  			Usage: "Disable content MD5 and SHA256 checksums for performance (default: off)",
   249  		},
   250  
   251  		cli.StringFlag{
   252  			Name:  "list-type",
   253  			Usage: "Listing type to use: ext-v1 (yandex only), 2 or 1 (default: ext-v1 for yandex, 1 for others)",
   254  			Value: "",
   255  		},
   256  
   257  		cli.StringFlag{
   258  			Name:  "multipart-age",
   259  			Usage: "Multipart uploads older than this value will be deleted on start",
   260  			Value: "48h",
   261  		},
   262  
   263  		cli.IntFlag{
   264  			Name:  "multipart-copy-threshold",
   265  			Usage: "Threshold for switching from single-part to multipart object copy in MB. Maximum for AWS S3 is 5 GB",
   266  			Value: 128,
   267  		},
   268  
   269  		/// http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
   270  		cli.StringFlag{
   271  			Name:  "acl",
   272  			Usage: "The canned ACL to apply to the object. Possible values: private, public-read, public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, bucket-owner-full-control (default: off)",
   273  			Value: "",
   274  		},
   275  
   276  		cli.BoolFlag{
   277  			Name:  "subdomain",
   278  			Usage: "Enable subdomain mode of S3",
   279  		},
   280  
   281  		cli.IntFlag{
   282  			Name:  "sdk-max-retries",
   283  			Value: 3,
   284  			Usage: "Maximum number of AWS SDK request retries.",
   285  		},
   286  
   287  		cli.DurationFlag{
   288  			Name:  "sdk-min-retry-delay",
   289  			Value: 30 * time.Millisecond,
   290  			Usage: "Minimum delay for AWS SDK retries of temporary request failures.",
   291  		},
   292  
   293  		cli.DurationFlag{
   294  			Name:  "sdk-max-retry-delay",
   295  			Value: 300 * time.Second,
   296  			Usage: "Maximum delay for AWS SDK retries of temporary request failures.",
   297  		},
   298  
   299  		cli.DurationFlag{
   300  			Name:  "sdk-min-throttle-delay",
   301  			Value: 500 * time.Millisecond,
   302  			Usage: "Minimum delay for AWS SDK retries of throttled requests (429, 502, 503, 504).",
   303  		},
   304  
   305  		cli.DurationFlag{
   306  			Name:  "sdk-max-throttle-delay",
   307  			Value: 300 * time.Second,
   308  			Usage: "Maximum delay for AWS SDK retries of throttled requests.",
   309  		},
   310  	}
   311  
   312  	tuningFlags := []cli.Flag{
   313  		/////////////////////////
   314  		// Tuning
   315  		/////////////////////////
   316  
   317  		cli.IntFlag{
   318  			Name:  "memory-limit",
   319  			Usage: "Maximum memory in MB to use for data cache",
   320  			Value: 1000,
   321  		},
   322  
   323  		cli.IntFlag{
   324  			Name:  "entry-limit",
   325  			Usage: "Maximum metadata entries to cache in memory (1 entry uses ~1 KB of memory)",
   326  			Value: 100000,
   327  		},
   328  
   329  		cli.IntFlag{
   330  			Name:  "gc-interval",
   331  			Usage: "Force garbage collection after this amount of data buffer allocations",
   332  			Value: 250,
   333  		},
   334  
   335  		cli.BoolFlag{
   336  			Name:  "cheap",
   337  			Usage: "Reduce S3 operation costs at the expense of some performance (default: off)",
   338  		},
   339  
   340  		cli.BoolFlag{
   341  			Name:  "no-implicit-dir",
   342  			Usage: "Assume all directory objects (\"dir/\") exist (default: off)",
   343  		},
   344  
   345  		cli.BoolFlag{
   346  			Name:  "no-dir-object",
   347  			Usage: "Do not create and check directory objects (\"dir/\") (default: off)",
   348  		},
   349  
   350  		cli.IntFlag{
   351  			Name:  "max-flushers",
   352  			Value: 16,
   353  			Usage: "How much parallel requests should be used for flushing changes to server",
   354  		},
   355  
   356  		cli.IntFlag{
   357  			Name:  "max-parallel-parts",
   358  			Value: 8,
   359  			Usage: "How much parallel requests out of the total number can be used for large part uploads."+
   360  				" Large parts take more bandwidth so they usually require less parallelism",
   361  		},
   362  
   363  		cli.IntFlag{
   364  			Name:  "max-parallel-copy",
   365  			Value: 16,
   366  			Usage: "How much parallel unmodified part copy requests should be used."+
   367  				" This limit is separate from max-flushers",
   368  		},
   369  
   370  		cli.IntFlag{
   371  			Name:  "read-ahead",
   372  			Value: 5*1024,
   373  			Usage: "How much data in KB should be pre-loaded with every read by default",
   374  		},
   375  
   376  		cli.IntFlag{
   377  			Name:  "small-read-count",
   378  			Value: 4,
   379  			Usage: "Number of last reads within a single file handle to be checked for being random",
   380  		},
   381  
   382  		cli.IntFlag{
   383  			Name:  "small-read-cutoff",
   384  			Value: 128,
   385  			Usage: "Maximum average size of last reads in KB to trigger \"small\" readahead",
   386  		},
   387  
   388  		cli.IntFlag{
   389  			Name:  "read-ahead-small",
   390  			Value: 128,
   391  			Usage: "Smaller readahead size in KB to be used when small random reads are detected",
   392  		},
   393  
   394  		cli.IntFlag{
   395  			Name:  "large-read-cutoff",
   396  			Value: 20*1024,
   397  			Usage: "Amount of linear read in KB after which the \"large\" readahead should be triggered",
   398  		},
   399  
   400  		cli.IntFlag{
   401  			Name:  "read-ahead-large",
   402  			Value: 100*1024,
   403  			Usage: "Larger readahead size in KB to be used when long linear reads are detected",
   404  		},
   405  
   406  		cli.IntFlag{
   407  			Name:  "read-ahead-parallel",
   408  			Value: 20*1024,
   409  			Usage: "Larger readahead will be triggered in parallel chunks of this size in KB",
   410  		},
   411  
   412  		cli.IntFlag{
   413  			Name:  "read-merge",
   414  			Value: 512,
   415  			Usage: "Two HTTP requests required to satisfy a read will be merged into one" +
   416  				" if they're at most this number of KB away",
   417  		},
   418  
   419  		cli.IntFlag{
   420  			Name:  "single-part",
   421  			Value: 5,
   422  			Usage: "Maximum size of an object in MB to upload it as a single part." +
   423  				" Can't be less than 5 MB",
   424  		},
   425  
   426  		cli.StringFlag{
   427  			Name:  "part-sizes",
   428  			Value: "5:1000,25:1000,125",
   429  			Usage: "Part sizes in MB. Total part count is always 10000 in S3."+
   430  				" Default is 1000 5 MB parts, then 1000 25 MB parts" +
   431  				" and then 125 MB for the rest of parts",
   432  		},
   433  
   434  		cli.BoolFlag{
   435  			Name:  "enable-patch",
   436  			Usage: "Use PATCH method to upload object data changes to s3. Yandex only. (default: off)",
   437  		},
   438  
   439  		cli.BoolFlag{
   440  			Name:  "drop-patch-conflicts",
   441  			Usage: "Drop local changes in case of conflicting concurrent PATCH updates. (default: off)",
   442  		},
   443  
   444  		cli.IntFlag{
   445  			Name:  "max-merge-copy",
   446  			Value: 0,
   447  			Usage: "If non-zero, allow to compose larger parts up to this number of megabytes" +
   448  				" in size from existing unchanged parts when doing server-side part copy."+
   449  				" Must be left at 0 for Yandex S3",
   450  		},
   451  
   452  		cli.BoolFlag{
   453  			Name:  "ignore-fsync",
   454  			Usage: "Do not wait until changes are persisted to the server on fsync() call (default: off)",
   455  		},
   456  
   457  		cli.BoolFlag{
   458  			Name:  "fsync-on-close",
   459  			Usage: "Wait until changes are persisted to the server when closing file (default: off)",
   460  		},
   461  
   462  		cli.BoolFlag{
   463  			Name:  "enable-perms",
   464  			Usage: "Enable permissions, user and group ID." +
   465  				" Only works correctly if your S3 returns UserMetadata in listings (default: off)",
   466  		},
   467  
   468  		cli.BoolFlag{
   469  			Name:  "enable-specials",
   470  			Usage: "Enable special file support (sockets, devices, named pipes)." +
   471  				" Only works correctly if your S3 returns UserMetadata in listings (default: on for Yandex, off for others)",
   472  		},
   473  
   474  		cli.BoolFlag{
   475  			Name:  "no-specials",
   476  			Usage: "Disable special file support (sockets, devices, named pipes).",
   477  		},
   478  
   479  		cli.BoolFlag{
   480  			Name:  "enable-mtime",
   481  			Usage: "Enable modification time preservation." +
   482  				" Only works correctly if your S3 returns UserMetadata in listings (default: off)",
   483  		},
   484  
   485  		cli.BoolFlag{
   486  			Name:  "disable-xattr",
   487  			Usage: "Disable extended attributes. Improves performance of very long directory listings",
   488  		},
   489  
   490  		cli.StringFlag{
   491  			Name:  "uid-attr",
   492  			Value: "uid",
   493  			Usage: "User ID metadata attribute name",
   494  		},
   495  
   496  		cli.StringFlag{
   497  			Name:  "gid-attr",
   498  			Value: "gid",
   499  			Usage: "Group ID metadata attribute name",
   500  		},
   501  
   502  		cli.StringFlag{
   503  			Name:  "mode-attr",
   504  			Value: "mode",
   505  			Usage: "File mode (permissions & special file flags) metadata attribute name",
   506  		},
   507  
   508  		cli.StringFlag{
   509  			Name:  "rdev-attr",
   510  			Value: "rdev",
   511  			Usage: "Block/character device number metadata attribute name",
   512  		},
   513  
   514  		cli.StringFlag{
   515  			Name:  "mtime-attr",
   516  			Value: "mtime",
   517  			Usage: "File modification time (UNIX time) metadata attribute name",
   518  		},
   519  
   520  		cli.StringFlag{
   521  			Name:  "symlink-attr",
   522  			Value: "--symlink-target",
   523  			Usage: "Symbolic link target metadata attribute name." +
   524  				" Only works correctly if your S3 returns UserMetadata in listings",
   525  		},
   526  
   527  		cli.StringFlag{
   528  			Name:  "refresh-attr",
   529  			Value: ".invalidate",
   530  			Usage: "Setting xattr with this name, without user. prefix, " +
   531  				" refreshes the cache of the file or directory.",
   532  		},
   533  
   534  		cli.DurationFlag{
   535  			Name:  "stat-cache-ttl",
   536  			Value: time.Minute,
   537  			Usage: "How long to cache file metadata.",
   538  		},
   539  
   540  		cli.DurationFlag{
   541  			Name:  "http-timeout",
   542  			Value: 30 * time.Second,
   543  			Usage: "Set the timeout on HTTP requests to S3",
   544  		},
   545  
   546  		cli.DurationFlag{
   547  			Name:  "retry-interval",
   548  			Value: 30 * time.Second,
   549  			Usage: "Retry unsuccessful writes after this time",
   550  		},
   551  
   552  		cli.DurationFlag{
   553  			Name:  "read-retry-interval",
   554  			Value: 1 * time.Second,
   555  			Usage: "Initial interval for retrying unsuccessful reads",
   556  		},
   557  
   558  		cli.Float64Flag{
   559  			Name:  "read-retry-mul",
   560  			Value: 2,
   561  			Usage: "Increase read retry interval this number of times on each unsuccessful attempt",
   562  		},
   563  
   564  		cli.DurationFlag{
   565  			Name:  "read-retry-max-interval",
   566  			Value: 60 * time.Second,
   567  			Usage: "Maximum interval for retrying unsuccessful reads",
   568  		},
   569  
   570  		cli.IntFlag{
   571  			Name:  "read-retry-attempts",
   572  			Value: 0,
   573  			Usage: "Maximum read retry attempts (0 means unlimited)",
   574  		},
   575  
   576  		cli.IntFlag{
   577  			Name:  "cache-popular-threshold",
   578  			Value: 3,
   579  			Usage: "Value of the read counter after which a cached file is started being treated as 'popular'",
   580  		},
   581  
   582  		cli.IntFlag{
   583  			Name:  "cache-max-hits",
   584  			Value: 6,
   585  			Usage: "Maximum value of the cache read counter for a file",
   586  		},
   587  
   588  		cli.IntFlag{
   589  			Name:  "cache-age-interval",
   590  			Value: 4096,
   591  			Usage: "Number of reads after which read counters are decremented for all files",
   592  		},
   593  
   594  		cli.IntFlag{
   595  			Name:  "cache-age-decrement",
   596  			Value: 1,
   597  			Usage: "Decrement amount",
   598  		},
   599  
   600  		cli.IntFlag{
   601  			Name:  "cache-to-disk-hits",
   602  			Value: 2,
   603  			Usage: "Minimum value of the read counter to cache file on disk",
   604  		},
   605  
   606  		cli.IntFlag{
   607  			Name:  "max-disk-cache-fd",
   608  			Value: 512,
   609  			Usage: "Simultaneously opened cache file descriptor limit",
   610  		},
   611  	}
   612  
   613  	if runtime.GOOS == "windows" {
   614  		tuningFlags = append(tuningFlags, cli.StringFlag{
   615  			Name:  "refresh-filename",
   616  			Value: ".invalidate",
   617  			Usage: "Trying to open a file with this name refreshes the cache of its directory.",
   618  		})
   619  		tuningFlags = append(tuningFlags, cli.StringFlag{
   620  			Name:  "flush-filename",
   621  			Value: ".fsyncdir",
   622  			Usage: "Trying to open a file with this name flushes the cache of its directory to server.",
   623  		})
   624  	}
   625  
   626  	debugFlags := []cli.Flag{
   627  		/////////////////////////
   628  		// Debugging
   629  		/////////////////////////
   630  
   631  		cli.BoolFlag{
   632  			Name:  "debug",
   633  			Usage: "Enable generic debugging output.",
   634  		},
   635  
   636  		cli.BoolFlag{
   637  			Name:  "debug_fuse",
   638  			Usage: "Enable fuse-related debugging output.",
   639  		},
   640  
   641  		cli.BoolFlag{
   642  			Name:  "debug_s3",
   643  			Usage: "Enable S3-related debugging output.",
   644  		},
   645  
   646  		cli.StringFlag{
   647  			Name:  "pprof",
   648  			Usage: "Specify port or host:port to enable pprof HTTP profiler on that port.",
   649  			Value: "",
   650  		},
   651  
   652  		cli.BoolFlag{
   653  			Name:  "f",
   654  			Usage: "Run geesefs in foreground.",
   655  		},
   656  
   657  		cli.StringFlag{
   658  			Name:  "log-file",
   659  			Usage: "Redirect logs to file, 'stderr' (default for foreground) or 'syslog' (default for background).",
   660  			Value: "",
   661  		},
   662  
   663  		cli.DurationFlag{
   664  			Name:  "print-stats",
   665  			Value: 30 * time.Second,
   666  			Usage: "I/O statistics printing interval. Set to 0 to disable.",
   667  		},
   668  
   669  		cli.BoolFlag{
   670  			Name:  "debug_grpc",
   671  			Usage: "Enable grpc logging in cluster mode.",
   672  		},
   673  	}
   674  
   675  	clusterFlags := []cli.Flag{
   676  		cli.BoolFlag{
   677  			Name: "cluster",
   678  			Usage: "Enable cluster mode.",
   679  		},
   680  
   681  		cli.BoolFlag{
   682  			Name: "grpc-reflection",
   683  			Usage: "Enable grpc reflection (--cluster flag required).",
   684  		},
   685  
   686  		cli.StringFlag{
   687  			Name: "cluster-me",
   688  			Usage: "<node-id>:<address> to communicate with this node (--cluster flag required).",
   689  		},
   690  
   691  		cli.StringSliceFlag{
   692  			Name: "cluster-peer",
   693  			Usage: "List of all cluster nodes in format <node-id>:<address> (--cluster flag required).",
   694  		},
   695  	}
   696  
   697  	app = &cli.App{
   698  		Name:     "geesefs",
   699  		Version:  GEESEFS_VERSION,
   700  		Usage:    "Mount an S3 bucket locally",
   701  		HideHelp: true,
   702  		Writer:   os.Stderr,
   703  		Flags:    append(append(append(append(append([]cli.Flag{
   704  			cli.BoolFlag{
   705  				Name:  "help, h",
   706  				Usage: "Print this help text and exit successfully.",
   707  			},
   708  		}, fsFlags...), s3Flags...), tuningFlags...), debugFlags...), clusterFlags...),
   709  	}
   710  
   711  	var funcMap = template.FuncMap{
   712  		"category": filterCategory,
   713  		"join":     strings.Join,
   714  	}
   715  
   716  	flagCategories = map[string]string{}
   717  	flagCategories["help"] = "misc"
   718  	flagCategories["h"] = "misc"
   719  
   720  	for _, f := range s3Flags {
   721  		for _, n := range strings.Split(f.GetName(), ",") {
   722  			flagCategories[strings.Trim(n, " ")] = "aws"
   723  		}
   724  	}
   725  	for _, f := range tuningFlags {
   726  		for _, n := range strings.Split(f.GetName(), ",") {
   727  			flagCategories[strings.Trim(n, " ")] = "tuning"
   728  		}
   729  	}
   730  	for _, f := range debugFlags {
   731  		for _, n := range strings.Split(f.GetName(), ",") {
   732  			flagCategories[strings.Trim(n, " ")] = "misc"
   733  		}
   734  	}
   735  
   736  	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
   737  		w = tabwriter.NewWriter(w, 1, 8, 2, ' ', 0)
   738  		var tmplGet = template.Must(template.New("help").Funcs(funcMap).Parse(templ))
   739  		tmplGet.Execute(w, app)
   740  	}
   741  
   742  	return
   743  }
   744  
   745  func parsePartSizes(s string) (result []PartSizeConfig) {
   746  	partSizes := strings.Split(s, ",")
   747  	totalCount := uint64(0)
   748  	for pi, ps := range partSizes {
   749  		a := strings.Split(ps, ":")
   750  		size, err := strconv.ParseUint(a[0], 10, 32)
   751  		if err != nil {
   752  			panic("Incorrect syntax for --part-sizes")
   753  		}
   754  		count := uint64(0)
   755  		if len(a) > 1 {
   756  			count, err = strconv.ParseUint(a[1], 10, 32)
   757  			if err != nil {
   758  				panic("Incorrect syntax for --part-sizes")
   759  			}
   760  		}
   761  		if count == 0 {
   762  			if pi < len(partSizes)-1 {
   763  				panic("Part count may be omitted only for the last interval")
   764  			}
   765  			count = 10000-totalCount
   766  		}
   767  		totalCount += count
   768  		if totalCount > 10000 {
   769  			panic("Total part count must be 10000")
   770  		}
   771  		if size < 5 {
   772  			panic("Minimum part size is 5 MB")
   773  		}
   774  		if size > 5*1024 {
   775  			panic("Maximum part size is 5 GB")
   776  		}
   777  		result = append(result, PartSizeConfig{
   778  			PartSize: size*1024*1024,
   779  			PartCount: count,
   780  		})
   781  	}
   782  	return
   783  }
   784  
   785  func parseNode(s string) *NodeConfig {
   786  	parts := strings.SplitN(s, ":", 2)
   787  	if len(parts) != 2 {
   788  		panic("Incorrect syntax for node config, should be: <node-id>:<address>")
   789  	}
   790  	nodeId, err := strconv.ParseUint(parts[0], 10, 64)
   791  	if err != nil {
   792  		panic("Incorrect syntax for node config, <node-id> shoud be uint64")
   793  	}
   794  	return &NodeConfig{
   795  		Id: nodeId,
   796  		Address: parts[1],
   797  	}
   798  }
   799  
   800  // PopulateFlags adds the flags accepted by run to the supplied flag set, returning the
   801  // variables into which the flags will parse.
   802  func PopulateFlags(c *cli.Context) (ret *FlagStorage) {
   803  	singlePart := c.Int("single-part")
   804  	if singlePart < 5 {
   805  		singlePart = 5
   806  	}
   807  
   808  	flags := &FlagStorage{
   809  		// File system
   810  		MountOptions:           c.StringSlice("o"),
   811  		DirMode:                os.FileMode(c.Int("dir-mode")),
   812  		FileMode:               os.FileMode(c.Int("file-mode")),
   813  		Uid:                    uint32(c.Int("uid")),
   814  		Gid:                    uint32(c.Int("gid")),
   815  		Setuid:                 c.Int("setuid"),
   816  		Setgid:                 c.Int("setgid"),
   817  
   818  		// Tuning,
   819  		MemoryLimit:            uint64(1024*1024*c.Int("memory-limit")),
   820  		EntryLimit:             c.Int("entry-limit"),
   821  		GCInterval:             uint64(1024*1024*c.Int("gc-interval")),
   822  		Cheap:                  c.Bool("cheap"),
   823  		ExplicitDir:            c.Bool("no-implicit-dir"),
   824  		NoDirObject:            c.Bool("no-dir-object"),
   825  		MaxFlushers:            int64(c.Int("max-flushers")),
   826  		MaxParallelParts:       c.Int("max-parallel-parts"),
   827  		MaxParallelCopy:        c.Int("max-parallel-copy"),
   828  		StatCacheTTL:           c.Duration("stat-cache-ttl"),
   829  		HTTPTimeout:            c.Duration("http-timeout"),
   830  		RetryInterval:          c.Duration("retry-interval"),
   831  		ReadRetryInterval:      c.Duration("read-retry-interval"),
   832  		ReadRetryMultiplier:    c.Float64("read-retry-mul"),
   833  		ReadRetryMax:           c.Duration("read-retry-max-interval"),
   834  		ReadRetryAttempts:      c.Int("read-retry-attempts"),
   835  		ReadAheadKB:            uint64(c.Int("read-ahead")),
   836  		SmallReadCount:         uint64(c.Int("small-read-count")),
   837  		SmallReadCutoffKB:      uint64(c.Int("small-read-cutoff")),
   838  		ReadAheadSmallKB:       uint64(c.Int("read-ahead-small")),
   839  		LargeReadCutoffKB:      uint64(c.Int("large-read-cutoff")),
   840  		ReadAheadLargeKB:       uint64(c.Int("read-ahead-large")),
   841  		ReadAheadParallelKB:    uint64(c.Int("read-ahead-parallel")),
   842  		ReadMergeKB:            uint64(c.Int("read-merge")),
   843  		SinglePartMB:           uint64(singlePart),
   844  		MaxMergeCopyMB:         uint64(c.Int("max-merge-copy")),
   845  		IgnoreFsync:            c.Bool("ignore-fsync"),
   846  		FsyncOnClose:           c.Bool("fsync-on-close"),
   847  		EnablePerms:            c.Bool("enable-perms"),
   848  		EnableSpecials:         c.Bool("enable-specials"),
   849  		EnableMtime:            c.Bool("enable-mtime"),
   850  		DisableXattr:           c.Bool("disable-xattr"),
   851  		UidAttr:                c.String("uid-attr"),
   852  		GidAttr:                c.String("gid-attr"),
   853  		FileModeAttr:           c.String("mode-attr"),
   854  		RdevAttr:               c.String("rdev-attr"),
   855  		MtimeAttr:              c.String("mtime-attr"),
   856  		SymlinkAttr:            c.String("symlink-attr"),
   857  		RefreshAttr:            c.String("refresh-attr"),
   858  		CachePath:              c.String("cache"),
   859  		MaxDiskCacheFD:         int64(c.Int("max-disk-cache-fd")),
   860  		CacheFileMode:          os.FileMode(c.Int("cache-file-mode")),
   861  		UsePatch:               c.Bool("enable-patch"),
   862  		DropPatchConflicts:     c.Bool("drop-patch-conflicts"),
   863  
   864  		// Common Backend Config
   865  		Endpoint:               c.String("endpoint"),
   866  		UseContentType:         c.Bool("use-content-type"),
   867  
   868  		// Debugging,
   869  		DebugMain:              c.Bool("debug"),
   870  		DebugFuse:              c.Bool("debug_fuse"),
   871  		DebugS3:                c.Bool("debug_s3"),
   872  		Foreground:             c.Bool("f"),
   873  		LogFile:                c.String("log-file"),
   874  		StatsInterval:          c.Duration("print-stats"),
   875  		PProf:                  c.String("pprof"),
   876  		DebugGrpc:              c.Bool("debug_grpc"),
   877  
   878  		// Cluster Mode
   879  		ClusterMode:            c.Bool("cluster"),
   880  		ClusterGrpcReflection:  c.Bool("grpc-reflection"),
   881  	}
   882  
   883  	if runtime.GOOS == "windows" {
   884  		flags.RefreshFilename = c.String("refresh-filename")
   885  		flags.FlushFilename = c.String("flush-filename")
   886  	}
   887  
   888  	flags.PartSizes = parsePartSizes(c.String("part-sizes"))
   889  
   890  	if flags.ClusterMode {
   891  		flags.ClusterMe = parseNode(c.String("cluster-me"))
   892  
   893  		for _, peer := range c.StringSlice("cluster-peer") {
   894  			flags.ClusterPeers = append(flags.ClusterPeers, parseNode(peer))
   895  		}
   896  	}
   897  
   898  	// S3 by default, if not initialized in api/api.go
   899  	if flags.Backend == nil {
   900  		flags.Backend = (&S3Config{}).Init()
   901  		config, _ := flags.Backend.(*S3Config)
   902  		config.Region        = c.String("region")
   903  		config.RegionSet     = c.IsSet("region")
   904  		config.ProjectId     = c.String("project-id")
   905  		config.RequesterPays = c.Bool("requester-pays")
   906  		config.StorageClass  = c.String("storage-class")
   907  		config.ColdMinSize   = c.Uint64("cold-min-size")
   908  		config.Profile       = c.String("profile")
   909  		config.SharedConfig  = c.StringSlice("shared-config")
   910  		config.UseSSE        = c.Bool("sse")
   911  		config.UseKMS        = c.IsSet("sse-kms")
   912  		config.KMSKeyID      = c.String("sse-kms")
   913  		config.SseC          = c.String("sse-c")
   914  		config.ACL           = c.String("acl")
   915  		config.Subdomain     = c.Bool("subdomain")
   916  		config.NoChecksum    = c.Bool("no-checksum")
   917  		config.UseIAM        = c.Bool("iam")
   918  		config.IAMHeader     = c.String("iam-header")
   919  		config.IAMFlavor     = c.String("iam-flavor")
   920  		config.IAMUrl        = c.String("iam-url")
   921  		config.MultipartAge  = c.Duration("multipart-age")
   922  		if config.IAMFlavor != "gcp" && config.IAMFlavor != "imdsv1" {
   923  			panic("Unknown --iam-flavor: "+config.IAMFlavor)
   924  		}
   925  		listType := c.String("list-type")
   926  		isYandex := strings.Contains(flags.Endpoint, "yandex")
   927  		if isYandex && !c.IsSet("no-specials") {
   928  			flags.EnableSpecials = true
   929  		}
   930  		if listType == "" {
   931  			if isYandex {
   932  				listType = "ext-v1"
   933  			} else {
   934  				listType = "1"
   935  			}
   936  		}
   937  		config.ListV1Ext     = listType == "ext-v1"
   938  		config.ListV2        = listType == "2"
   939  
   940  		config.MultipartCopyThreshold = uint64(c.Int("multipart-copy-threshold")) * 1024 * 1024
   941  
   942  		config.SDKMaxRetries = c.Int("sdk-max-retries")
   943  		config.SDKMinRetryDelay = c.Duration("sdk-min-retry-delay")
   944  		config.SDKMaxRetryDelay = c.Duration("sdk-max-retry-delay")
   945  		config.SDKMinThrottleDelay = c.Duration("sdk-min-throttle-delay")
   946  		config.SDKMaxThrottleDelay = c.Duration("sdk-max-throttle-delay")
   947  
   948  		// KMS implies SSE
   949  		if config.UseKMS {
   950  			config.UseSSE = true
   951  		}
   952  	}
   953  
   954  	if c.IsSet("no-specials") {
   955  		flags.EnableSpecials = false
   956  	}
   957  
   958  	if syscall.Getuid() == 0 && !c.IsSet("setuid") && flags.Uid != 0 {
   959  		flags.Setuid = int(flags.Uid)
   960  	}
   961  	if syscall.Getgid() == 0 && !c.IsSet("setgid") && flags.Gid != 0 {
   962  		flags.Setgid = int(flags.Gid)
   963  	}
   964  
   965  	flags.MountPointArg = c.Args()[1]
   966  	flags.MountPoint = flags.MountPointArg
   967  	var err error
   968  
   969  	defer func() {
   970  		if err != nil {
   971  			flags.Cleanup()
   972  		}
   973  	}()
   974  
   975  	if !flags.ClusterMode && flags.ClusterGrpcReflection {
   976  		return nil
   977  	}
   978  
   979  	if flags.ClusterMode != (flags.ClusterMe != nil) {
   980  		return nil
   981  	}
   982  
   983  	if flags.ClusterMode != (flags.ClusterPeers != nil) {
   984  		return nil
   985  	}
   986  
   987  	return flags
   988  }
   989  
   990  func MessageMountFlags(args []string) (ret []string) {
   991  	if len(args) == 5 && args[3] == "-o" {
   992  		// looks like it's coming from fstab!
   993  		mountOptions := ""
   994  		ret = append(ret, args[0])
   995  
   996  		for _, p := range strings.Split(args[4], ",") {
   997  			if strings.HasPrefix(p, "-") {
   998  				ret = append(ret, p)
   999  			} else {
  1000  				mountOptions += p
  1001  				mountOptions += ","
  1002  			}
  1003  		}
  1004  
  1005  		if len(mountOptions) != 0 {
  1006  			// remove trailing ,
  1007  			mountOptions = mountOptions[:len(mountOptions)-1]
  1008  			ret = append(ret, "-o")
  1009  			ret = append(ret, mountOptions)
  1010  		}
  1011  
  1012  		ret = append(ret, args[1])
  1013  		ret = append(ret, args[2])
  1014  	} else {
  1015  		return args
  1016  	}
  1017  
  1018  	return
  1019  }
  1020  
  1021  func DefaultFlags() *FlagStorage {
  1022  	uid, gid := MyUserAndGroup()
  1023  	return &FlagStorage{
  1024  		DirMode:                0755,
  1025  		FileMode:               0644,
  1026  		CacheFileMode:          0644,
  1027  		Uid:                    uint32(uid),
  1028  		Gid:                    uint32(gid),
  1029  		Setuid:                 uid,
  1030  		Setgid:                 gid,
  1031  		Endpoint:               "https://storage.yandexcloud.net",
  1032  		Backend:                (&S3Config{}).Init(),
  1033  		MemoryLimit:            1000 * 1024 * 1024,
  1034  		EntryLimit:             100000,
  1035  		GCInterval:             250 * 1024 * 1024,
  1036  		MaxFlushers:            16,
  1037  		MaxParallelParts:       8,
  1038  		MaxParallelCopy:        16,
  1039  		ReadAheadKB:            5 * 1024,
  1040  		SmallReadCount:         4,
  1041  		SmallReadCutoffKB:      128,
  1042  		ReadAheadSmallKB:       128,
  1043  		LargeReadCutoffKB:      20 * 1024,
  1044  		ReadAheadLargeKB:       100 * 1024,
  1045  		ReadAheadParallelKB:    20 * 1024,
  1046  		ReadMergeKB:            512,
  1047  		SinglePartMB:           5,
  1048  		MaxMergeCopyMB:         0,
  1049  		UidAttr:                "uid",
  1050  		GidAttr:                "gid",
  1051  		FileModeAttr:           "mode",
  1052  		RdevAttr:               "rdev",
  1053  		MtimeAttr:              "mtime",
  1054  		SymlinkAttr:            "--symlink-target",
  1055  		RefreshAttr:            ".invalidate",
  1056  		StatCacheTTL:           30 * time.Second,
  1057  		HTTPTimeout:            30 * time.Second,
  1058  		RetryInterval:          30 * time.Second,
  1059  		MaxDiskCacheFD:         512,
  1060  		RefreshFilename:        ".invalidate",
  1061  		FlushFilename:          ".fsyncdir",
  1062  		PartSizes: []PartSizeConfig{
  1063  			{PartSize: 5 * 1024 * 1024, PartCount: 1000},
  1064  			{PartSize: 25 * 1024 * 1024, PartCount: 1000},
  1065  			{PartSize: 125 * 1024 * 1024, PartCount: 8000},
  1066  		},
  1067  	}
  1068  }