1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.cgminer;
9
10 convType = with builtins; v: if lib.isBool v then lib.boolToString v else toString v;
11 mergedHwConfig = lib.mapAttrsToList (
12 n: v: ''"${n}": "${(lib.concatStringsSep "," (map convType v))}"''
13 ) (lib.foldAttrs (n: a: [ n ] ++ a) [ ] cfg.hardware);
14 mergedConfig =
15 with builtins;
16 lib.mapAttrsToList (
17 n: v: ''"${n}": ${if lib.isBool v then convType v else ''"${convType v}"''}''
18 ) cfg.config;
19
20 cgminerConfig = pkgs.writeText "cgminer.conf" ''
21 {
22 ${lib.concatStringsSep ",\n" mergedHwConfig},
23 ${lib.concatStringsSep ",\n" mergedConfig},
24 "pools": [
25 ${
26 lib.concatStringsSep ",\n" (
27 map (v: ''{"url": "${v.url}", "user": "${v.user}", "pass": "${v.pass}"}'') cfg.pools
28 )
29 }]
30 }
31 '';
32in
33{
34 ###### interface
35 options = {
36
37 services.cgminer = {
38
39 enable = lib.mkEnableOption "cgminer, an ASIC/FPGA/GPU miner for bitcoin and litecoin";
40
41 package = lib.mkPackageOption pkgs "cgminer" { };
42
43 user = lib.mkOption {
44 type = lib.types.str;
45 default = "cgminer";
46 description = "User account under which cgminer runs";
47 };
48
49 pools = lib.mkOption {
50 default = [ ]; # Run benchmark
51 type = lib.types.listOf (lib.types.attrsOf lib.types.str);
52 description = "List of pools where to mine";
53 example = [
54 {
55 url = "http://p2pool.org:9332";
56 username = "17EUZxTvs9uRmPsjPZSYUU3zCz9iwstudk";
57 password = "X";
58 }
59 ];
60 };
61
62 hardware = lib.mkOption {
63 default = [ ]; # Run without options
64 type = lib.types.listOf (lib.types.attrsOf (lib.types.either lib.types.str lib.types.int));
65 description = "List of config options for every GPU";
66 example = [
67 {
68 intensity = 9;
69 gpu-engine = "0-985";
70 gpu-fan = "0-85";
71 gpu-memclock = 860;
72 gpu-powertune = 20;
73 temp-cutoff = 95;
74 temp-overheat = 85;
75 temp-target = 75;
76 }
77 {
78 intensity = 9;
79 gpu-engine = "0-950";
80 gpu-fan = "0-85";
81 gpu-memclock = 825;
82 gpu-powertune = 20;
83 temp-cutoff = 95;
84 temp-overheat = 85;
85 temp-target = 75;
86 }
87 ];
88 };
89
90 config = lib.mkOption {
91 default = { };
92 type = lib.types.attrsOf (lib.types.either lib.types.bool lib.types.int);
93 description = "Additional config";
94 example = {
95 auto-fan = true;
96 auto-gpu = true;
97 expiry = 120;
98 failover-only = true;
99 gpu-threads = 2;
100 log = 5;
101 queue = 1;
102 scan-time = 60;
103 temp-histeresys = 3;
104 };
105 };
106 };
107 };
108
109 ###### implementation
110
111 config = lib.mkIf config.services.cgminer.enable {
112
113 users.users = lib.optionalAttrs (cfg.user == "cgminer") {
114 cgminer = {
115 isSystemUser = true;
116 group = "cgminer";
117 description = "Cgminer user";
118 };
119 };
120 users.groups = lib.optionalAttrs (cfg.user == "cgminer") {
121 cgminer = { };
122 };
123
124 environment.systemPackages = [ cfg.package ];
125
126 systemd.services.cgminer = {
127 path = [ pkgs.cgminer ];
128
129 after = [
130 "network.target"
131 "display-manager.service"
132 ];
133 wantedBy = [ "multi-user.target" ];
134
135 environment = {
136 LD_LIBRARY_PATH = "/run/opengl-driver/lib:/run/opengl-driver-32/lib";
137 DISPLAY = ":${toString config.services.xserver.display}";
138 GPU_MAX_ALLOC_PERCENT = "100";
139 GPU_USE_SYNC_OBJECTS = "1";
140 };
141
142 startLimitIntervalSec = 60; # 1 min
143 serviceConfig = {
144 ExecStart = "${pkgs.cgminer}/bin/cgminer --syslog --text-only --config ${cgminerConfig}";
145 User = cfg.user;
146 RestartSec = "30s";
147 Restart = "always";
148 };
149 };
150
151 };
152
153}