-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
710 lines (703 loc) · 49.8 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://bofeng.github.io</id>
<title>0xBF</title>
<updated>2021-01-11T00:57:20.797Z</updated>
<generator>https://github.com/jpmonette/feed</generator>
<link rel="alternate" href="https://bofeng.github.io"/>
<link rel="self" href="https://bofeng.github.io/atom.xml"/>
<logo>https://bofeng.github.io/images/avatar.png</logo>
<icon>https://bofeng.github.io/favicon.ico</icon>
<rights>All rights reserved 2021, 0xBF</rights>
<entry>
<title type="html"><![CDATA[Play w/ IPFS]]></title>
<id>https://bofeng.github.io/post/play-with-ipfs/</id>
<link href="https://bofeng.github.io/post/play-with-ipfs/">
</link>
<updated>2021-01-10T10:48:02.000Z</updated>
<content type="html"><![CDATA[<p>IPFS is a p2p file system.</p>
<h2 id="installation">Installation</h2>
<p>I am using Pop! OS which is a Ubuntu based, so intall the ipfs is pretty easy:</p>
<pre><code class="language-bash">snap install ipfs
</code></pre>
<p><code>ipfs</code> stores settings and files in repo, so if this is the first time you install ipfs, you need to initialize it <code>ipfs init</code>.</p>
<h2 id="add-file">Add file</h2>
<p>Now you can use <code>ipfs</code> to add file:</p>
<pre><code class="language-bash">$ ipfs add feeds.json
added QmYzTnTfxduwx1N88MyUg4Jushbm7pw1WPSSedMz8WP9RY feeds.json
</code></pre>
<p>The <code>QmYzTnTfxduwx1N88MyUg4Jushbm7pw1WPSSedMz8WP9RY</code> is your file's hash.</p>
<h2 id="take-node-online">Take node online</h2>
<pre><code class="language-bash">$ ipfs daemon
...
API server listening on /ip4/127.0.0.1/tcp/5001
WebUI: http://127.0.0.1:5001/webui
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready
</code></pre>
<p>Now your IPFS node is online, your node server is by default running at 5001 port, and the gateway is running at 8080 port. The daemon provides a WebUI which you can directly visit in your browser: http://127.0.0.1:5001/webui<br>
Also since your node is online now, other peers can detect and connect to your node.<br>
See your peers address with command <code>ipfs swarm peers</code></p>
<p>And for the file you added, its address is <code>ipfs://QmYzTnTfxduwx1N88MyUg4Jushbm7pw1WPSSedMz8WP9RY</code>, but since brower doesn't understand the protocol, it won't know how to open the file - that's where the gateway does it work - the address will fall back to <code>http://localhost:8080/ipfs/QmYzTnTfxduwx1N88MyUg4Jushbm7pw1WPSSedMz8WP9RY</code>. Notice the localhost:8080 is your gateway address, and browser support http protocol, so now you can see your file in your browser. Plus, the best thing is, since it is a P2P network, other nodes can detect and cache your file, you can change it to other gateways, like <code>http://ipfs.io/ipfs/QmYzTnTfxduwx1N88MyUg4Jushbm7pw1WPSSedMz8WP9RY</code></p>
<p>Other popular public gateways are:</p>
<ul>
<li>dweb.link</li>
<li>cloudflare-ipfs.com</li>
<li>cf-ipfs.com</li>
</ul>
<p>More public gateway domains you can find it <a href="https://ipfs.github.io/public-gateway-checker/">here</a>.</p>
<h2 id="add-folder">Add folder</h2>
<p>You can host your static files/website, say you have a folder named "website" which contains your static files:</p>
<ul>
<li>index.html</li>
<li>cook.jpg</li>
</ul>
<p>Code in the <code>index.html</code>:</p>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<title>0xBF's play ground</title>
</head>
<body>
<center>
<h1>IPFS Play Ground</h1>
<h2>tasty salad</h2>
<p>
<img src="cook.jpg" width="600px">
<p>
</center>
</body>
</html>
</code></pre>
<p>Now put the whole folder online:</p>
<pre><code class="language-bash">$ ipfs add -r website
added QmTJtZJAmiVoCo7b8LDLHKoQAt5DBFeFc6D2mPxYiEkGVx website/cook.jpg
added QmUqn9j8j5S557jdZuT5yj9dntk7wzWpZbFW5y838M3PVf website/index.html
added QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL website
</code></pre>
<p>The last hash <code>QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL</code> if your folder's IPFS address. If you visit <code>http://localhost:8080/ipfs/QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL</code> you will be able to see your site. And if you change the gateway address to some other public gateway address, it will work too (like https://ipfs.io/ipfs/QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL)</p>
<h2 id="ipns">IPNS</h2>
<p>If you made some change to the files in website folder, or add some other files, which is not uncommon to update the website, you need to do the <code>ipfs add -r</code> again, but you will find that the folder's hash has changed, now the website address is <code>https://ipfs.io/ipfs/<some other hash value></code>. This is very inconvenient if you shared your website address to others, and after you updated the files, your website address changed and you need to re-share the url address. IPNS can help to solve this problem.</p>
<p>IPNS is a "name server", like the DNS system which point a domain name to some ip address. IPNS is used for pointing a fixed <code>ipns</code> hash to a <code>ipfs</code> hash.</p>
<p>To use the IPNS you need to do <code>name publish</code>. Take the above for example, we know the website's <strong>ipfs</strong> hash is <code>QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL</code>, now just need to publish it:</p>
<pre><code class="language-bash">$ ipfs name publish QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL
Published to k51qzi5uqu5dg85apeizh0th2b008n2xmywwnf96coy9an98dd0k1esuyxi0zw: /ipfs/QmdUbiyC685LKYZkJCXfZsMnntku9DoX2PAuJsQtKwv5HL
</code></pre>
<p>Now we get the <strong>ipns</strong> hash: <code>k51qzi5uqu5dg85apeizh0th2b008n2xmywwnf96coy9an98dd0k1esuyxi0zw</code><br>
Then you can visit your website at: <code>https://ipfs.io/ipns/k51qzi5uqu5dg85apeizh0th2b008n2xmywwnf96coy9an98dd0k1esuyxi0zw</code> , notice here in the middle of the path, it is <code>ipns</code>, not <code>ipfs</code>.</p>
<p>Say now we made some change to our website folder, let's add it again to IPFS:</p>
<pre><code class="language-bash">$ ipfs add -r website
added QmTJtZJAmiVoCo7b8LDLHKoQAt5DBFeFc6D2mPxYiEkGVx website/cook.jpg
added QmeFPgorCSs6F2u58hmnujKaW9h7GopuXWHgvDTn3vkCsy website/index.html
added Qmd3yZhAKsyFuSgG81d3VM8nRUyzrfttsyZXka4AfPgAWP website
</code></pre>
<p><code>Qmd3yZhAKsyFuSgG81d3VM8nRUyzrfttsyZXka4AfPgAWP</code> is the new <strong>ipfs</strong> hash for our website folder. We need to publish it again:</p>
<pre><code class="language-bash">$ ipfs name publish Qmd3yZhAKsyFuSgG81d3VM8nRUyzrfttsyZXka4AfPgAWP
Published to k51qzi5uqu5dg85apeizh0th2b008n2xmywwnf96coy9an98dd0k1esuyxi0zw: /ipfs/Qmd3yZhAKsyFuSgG81d3VM8nRUyzrfttsyZXka4AfPgAWP
</code></pre>
<p>You can see the ipns hash stay unchanged. Now you fresh the above ipns address (https://ipfs.io/ipns/k51qzi5uqu5dg85apeizh0th2b008n2xmywwnf96coy9an98dd0k1esuyxi0zw)in the browser, you will be able to see the updated version. Under the hood, the ipns address now points to the new ipfs hash.</p>
<h2 id="when-your-server-is-offline">When your server is offline</h2>
<p>You can take your server down, in the command line, just use Ctrl-C to quit the <code>ipfs daemon</code>. After that, your file might still be visited from other gateway or nodes. It depends on how other node handle your file. For example, after you visit the above ipfs.io gateway will cache your files for several hours, so even your server is taken down, you can still visit your website at that address for several hours until the gameway ipfs.io purge its cache. If the node do further - not only cache your file but also <code>pin</code> your file - your file will be stored in their server, then you can keep visiting the address until other nodes take it down.</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://docs.ipfs.io/install/command-line/">Installation</a></li>
<li><a href="https://dweb-primer.ipfs.io/publishing-changes/modify-republish">Modify Your Webpage and Republish to IPNS</a></li>
<li><a href="https://github.com/ipfs/ipfs">IPFS Repo</a></li>
<li><a href="https://docs.ipfs.io/install/ipfs-companion/">IPFS Companion</a></li>
<li><a href="https://discuss.ipfs.io/t/after-my-local-server-stops-hosting-the-file-where-does-the-file-go/1438">After my local server stops hosting the file, where does the file go?</a></li>
<li><a href="https://medium.com/python-pandemonium/getting-started-with-python-and-ipfs-94d14fdffd10">Getting started with Python and IPFS</a></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Get real client ip when server is behind proxies]]></title>
<id>https://bofeng.github.io/post/get-real-client-ip-when-server-behind-proxy/</id>
<link href="https://bofeng.github.io/post/get-real-client-ip-when-server-behind-proxy/">
</link>
<updated>2020-09-03T06:38:45.000Z</updated>
<content type="html"><![CDATA[<h2 id="server-architecture-v1">Server Architecture V1</h2>
<p>I was starting to use the DO's load balancer, my server architecture is like this:<br>
<img src="https://bofeng.github.io/post-images/1599244822913.png" alt="" loading="lazy"></p>
<p>I have Nginx running on my Droplets, and to get the real IP, we first need to config the DO's load balancer:</p>
<ol>
<li>go to Networking > Load Balancers, select your balancer</li>
<li>Under settings tab, click "Proxy Protocol" and enable it.</li>
</ol>
<p>Now in the Nginx on the droplet, first we need to enable the proxy protocol:</p>
<pre><code class="language-nginx">server {
listen 80 proxy_protocol;
...
}
</code></pre>
<p>Now the website should work now behind the load balancer. But now in the server access log, the client IP is logged as the load balancer's IP, which is not what I want. I want to get the real client IP so I can do further geo location based feature.</p>
<p>To get the real ip, first need to check if the Nginx has real_ip module installed.</p>
<pre><code class="language-bash">nginx -V 2>&1 | grep -- 'http_realip_module'
nginx -V 2>&1 | grep -- 'stream_realip_module'
</code></pre>
<p>I was using <code>apt install nginx</code> to install the default nginx, and the <code>http_realip_module</code> is installed by default. I don't have the second one, but I don't need it at this moment.</p>
<p>Now back to our website's nginx config file:</p>
<pre><code class="language-nginx">set_real_ip_from 10.108.0.0/20;
real_ip_header X-Forwarded-For;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
log_format mycombined '$proxy_protocol_addr - $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
server {
...
access_log /var/log/nginx/access.log mycombined;
}
</code></pre>
<p>Here we use <code>set_real_ip_from</code> to define an IP range to indicate when a request is from this IP (my load balancer in this case), extra the real_ip_header field from the "X-Forwarded-For" field in the header. And also set the <code>X-Fowarded-For</code> header in order to forward this request to our real application handler (like Django or Starlette in my case). We also define a log_format which included the proxy_protocol and remote_addr. In our above architecture v1 case, these 2 IPs will be the same.</p>
<blockquote>
<p>... the $remote_addr and $remote_port variables retain the real IP address and port of the client, while the $realip_remote_addr and $realip_remote_port variables retain the IP address and port of the load balancer <sup>[1]</sup></p>
</blockquote>
<p>Once we define the log_format, we can use it for our access log. Also, if you want to limit some IP access, because we already extract the real ip from the header, you can add the allow/deny rules like normal in Nginx:</p>
<pre><code>allow 203.0.113.10;
deny all;
</code></pre>
<p><strong>Also, to secure the Droplet</strong>, we'd better set some firewall rules in the Droplet server such like only allow port 80 connections from the Load Balancer. We can set this Firewall rule in DO's "Networking" > "Firewalls" Page.</p>
<h2 id="server-architecture-v2">Server Architecture V2</h2>
<p>Later I want to use CloudFlare to put in front of my DO's load balancer, yep, a proxy in front of another proxy, to prevent DDoS. CloudFlare has a good name on this.</p>
<p>So first I need to transfer my domain's DNS to CloudFlare, and the server architecture like this:</p>
<figure data-type="image" tabindex="1"><img src="https://bofeng.github.io/post-images/1599246314807.png" alt="" loading="lazy"></figure>
<p>Now we need to change our Nginx settings to get the client's real IP b/c there are 2 layer of proxies. According to the doc:</p>
<blockquote>
<p>CF-Connecting-IP<br>
Provides the client (visitor) IP address (connecting to Cloudflare) to the origin web server. <sup>3</sup></p>
</blockquote>
<p>So we can get the client ip from the <code>CF-Connecting-IP</code> header field. Let's change our configs in Nginx:</p>
<pre><code>set_real_ip_from 10.108.0.0/20;
real_ip_header CF-Connecting-IP;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
</code></pre>
<p>Now we grab the real_ip from the <code>CF-Connecting-IP</code> header, and set that IP to X-Real-IP header for our real app handlers. Please notice now that the <code>$proxy_protocol_addr</code> and <code>remote_addr</code> are different now. The former one is CloudFlare's proxy server IP and the latter one is the client's real IP. You can see that in the access log:</p>
<pre><code>108.163.218.1 - 203.0.113.10 - - [04/Sep/2020:19:11:11 +0000] "GET / HTTP/1.1" 200 396 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0"
</code></pre>
<p>Two other things:</p>
<ol>
<li>
<p>Now if we want to limit some IP access, we don't have to add the IP in the Nginx config. We can directly set a Firewall rule in CloudFlare. Just go to the "Firewall Rules" tab to add a new Firewall rule, like this:<br>
<img src="https://bofeng.github.io/post-images/1599246920955.png" alt="" loading="lazy"></p>
</li>
<li>
<p>CloudFlare will auto generate a SSL for our domain. When user visit CloudFlare's proxy server, the connection is encrypted, then CloudFlare will proxy that request to our load balancer, so this part connection should also be encrypted. We need to add the forwarding rule to DO's load balancer:</p>
<ol>
<li>Generate SSL cert in CloudFlare: go to SSL/TLS table, click "Origin Server", click "create certificate"</li>
<li>Grab the certificate data, copy and paste them to DO's load balancer settings: In DO's load balancer settings, click the "forwarding rules", add forwarding HTTPS:443 to HTTP:80 (our droplet), and one of the field will require you add SSL certificate, click "+ New Certificate" > "Bring your own cerficate", in there paste your SSL cert generated from the previous step.</li>
</ol>
</li>
</ol>
<figure data-type="image" tabindex="2"><img src="https://bofeng.github.io/post-images/1599247516756.png" alt="" loading="lazy"></figure>
<p>Now the architecture looks good: CloudFlare will prevent DDoS, and forward secure data to DO's load balancer, then the load balancer will forward data to backend droplets, and our droplets only allow connections from the load balancer, so it cannot be acessed from other public IP, which make it very secure, plus via the Nginx settings, we are still able to grab Client's real IP behind 2 layers proxies. Hooray!</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/">Nginx Proxy Settings</a></li>
<li><a href="https://www.digitalocean.com/docs/networking/load-balancers/">DigitalOcean's load balancer doc</a></li>
<li><a href="https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-">CloudFlare Proxy Settings</a></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MacOS的Battery显示充电状态不对的问题]]></title>
<id>https://bofeng.github.io/post/macos-de-battery-xian-shi-chong-dian-zhuang-tai-bu-dui-de-wen-ti/</id>
<link href="https://bofeng.github.io/post/macos-de-battery-xian-shi-chong-dian-zhuang-tai-bu-dui-de-wen-ti/">
</link>
<updated>2020-08-12T15:51:27.000Z</updated>
<content type="html"><![CDATA[<p>已经第二次遇到这个问题了,MBP显示只剩1%的电了,就算插着充电器,也充不进电,而且电量一直在下降。就算重启也没用。<br>
重置了电池的SMC之后就好了,方法:<br>
1,关机<br>
2,按住Ctrl + Shift + Option + Power Button 五秒<br>
3,按Power Button开机<br>
然后再充电,电池状态就显示正常了。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Setup wireguard VPN]]></title>
<id>https://bofeng.github.io/post/setup-wireguard-vpn/</id>
<link href="https://bofeng.github.io/post/setup-wireguard-vpn/">
</link>
<updated>2020-05-26T09:18:24.000Z</updated>
<content type="html"><![CDATA[<p>It is really easy to configure wireguard VPN, compare to the old IPsec and OpenVPN methods.</p>
<p>In wireguard method, there is no server/client, they are all peers. So a "server" is a peer, a client is also a peer. If we want to let multi clients to connect to a server, they are actually multi peers connecting to one peer (server).</p>
<h2 id="1-server-configuration">1, Server configuration</h2>
<p>On ubuntu < 19.10, you need to do <code>sudo add-apt-repository ppa:wireguard/wireguard</code> first. Version >= 19.10, you can just run: <code>sudo apt install wireguard</code></p>
<p>Once done, generate private-key and public-key pair for your server:</p>
<pre><code class="language-bash">$ wg genkey
# this will generate a private key, e.g. sL9JUT8axlP4wr6udFCraTLxIVNnYLKHNKtxt/JC42w=
# now use this private key to generate a public key
$ echo "sL9JUT8axlP4wr6udFCraTLxIVNnYLKHNKtxt/JC42w=" | wg pubkey
# this will generate a public key, e.g. +cITDHN+T/AseY9eA4SuZQilDUALxC0lCwtHNC6L8yU=
</code></pre>
<p>Now go to <code>/etc/wireguard/</code> folder, add a config file: <code>wg0.conf</code>:</p>
<pre><code>[Interface]
Address = 10.0.0.1/24
DNS = 1.1.1.1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = sL9JUT8axlP4wr6udFCraTLxIVNnYLKHNKtxt/JC42w=
</code></pre>
<p>In the <code>PostUp</code> and <code>PostDown</code> step, replace <code>eth0</code> if your network is running on a different interface.</p>
<p>Here we are using 51820 as the port. If you are using <code>ufw</code>, remember to unblock this port (wireguard is running on UDP protocol)</p>
<pre><code class="language-bash">$ ufw allow 51820/udp
$ ufw status
</code></pre>
<h2 id="2-client-setup">2, Client setup</h2>
<p>Client is another peer. Install client here: <a href="https://www.wireguard.com/install/">https://www.wireguard.com/install/</a></p>
<p>Wireguard uses public key to identify a peer, so the first setup is still generate private/public key pair:</p>
<ul>
<li>MacOS user: open the Wireguard app, click "Add Tunnel" > "Empty Tunnel", a window will pop-up to show generated private/public key, save those keys somewhere, then click "Discard" (Don't click save)</li>
<li>Windows user: open the Wireguard app, click "Add Tunnel" > "Empty Tunnel", a window will pop-up to show generated private/public key, save those keys somewhere, then click "Cancel" (Don't click save)</li>
<li>Ubuntu user: use the same way which used by server in above step. i.e. the "wg genkey" etc. method.</li>
</ul>
<p>Once you have the private key and public key, create a config file anywhere (If you are using Ubuntu, put that config in <code>/etc/wireguard/wgclient.conf</code>), the content is like this:</p>
<pre><code>[Interface]
DNS = 1.1.1.1
PrivateKey = <your client private key>
Address = 10.0.0.2/32
# if you have multi client across different devices, use different address here like:
# 10.0.0.3/32, 10.0.0.4/32, etc
[Peer]
PublicKey = <server's public key which is generated in the first step>
Endpoint = <server's ip>:51820
AllowedIPs = 0.0.0.0/0, ::/0
</code></pre>
<p>Save this <code>wgclient.conf</code> file, for MacOS and Windows user, click "Add Tunnel" > "Add Tunnel from existing config file", then select this config file. After this, configuration on the client side are all set.</p>
<h2 id="3-add-client-to-server">3, Add client to server</h2>
<p>Now we have client's private key and public key, it is time to add the client public key to server. Back to our <code>/etc/wireguard/wg0.conf</code> file in server side, then add:</p>
<pre><code>[Peer]
# client 1
PublicKey = <client1's public key>
AllowedIPs = 10.0.0.2/32
[Peer]
# client 2
PublicKey = <client2's public key>
AllowedIPs = 10.0.0.3/32
</code></pre>
<p>Now activate the wireguard in server side:</p>
<pre><code class="language-bash"># activate
$ wg-quick up wg0
# to deactivate it, run:
# $ wg-quick down wg0
</code></pre>
<h2 id="4-connect-client-to-server">4, Connect client to server</h2>
<p>For MacOS and Windows user, since we have added the configuration file in step 2, all you need to do is click the "activate" button. For Ubuntu user, just run <code>wg-quick up wgclient</code>. Once done, your client peer should be now connected to the server peer.</p>
<p>On server side you can run <code>wg show</code> to check the connected peers status.</p>
<p>To disconnect it, MacOS and Windows user can just click the "deactivate" button. Ubuntu user can run <code>wg-quick down wgclient</code>.</p>
<h2 id="others">Others</h2>
<p>1, To make wireguard auto run on system boot, run:</p>
<pre><code class="language-bash">sudo systemctl enable wg-quick@wg0
</code></pre>
<p>2, If you have some website running in your VPN server and you want to access it with wireguard VPN connected, the access IP now will be the private IP (e.g. 10.0.0.2, 10.0.0.3). If you website have an IP whitelist, you need to list those IPs as well. For example, in Nginx, you can add:</p>
<pre><code>allow 10.0.0.1;
allow 10.0.0.2;
</code></pre>
<p>You can also add a range, e.g. if you want to allow 10.0.0.1 to 10.0.0.19, a "IP to CIDR" address tool can help: <a href="https://www.ipaddressguide.com/cidr#range">https://www.ipaddressguide.com/cidr#range</a>. For example, for IP range 10.0.0.1 to 10.0.0.19, you can add <code>allow</code> like this:</p>
<pre><code>allow 10.0.0.1/32;
allow 10.0.0.2/31;
allow 10.0.0.4/30;
allow 10.0.0.8/29;
allow 10.0.0.16/30;
</code></pre>
<h2 id="reference">Reference</h2>
<ol>
<li><a href="https://www.linode.com/docs/networking/vpn/set-up-wireguard-vpn-on-ubuntu/">https://www.linode.com/docs/networking/vpn/set-up-wireguard-vpn-on-ubuntu/</a></li>
<li><a href="https://tau.gr/posts/2019-03-02-set-up-wireguard-vpn-ubuntu-mac/">https://tau.gr/posts/2019-03-02-set-up-wireguard-vpn-ubuntu-mac/</a></li>
<li><a href="https://gist.github.com/chrisswanda/88ade75fc463dcf964c6411d1e9b20f4">https://gist.github.com/chrisswanda/88ade75fc463dcf964c6411d1e9b20f4</a></li>
</ol>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[rocket.chat server stopped on an update]]></title>
<id>https://bofeng.github.io/post/rocketchat-server-stopped-on-an-update/</id>
<link href="https://bofeng.github.io/post/rocketchat-server-stopped-on-an-update/">
</link>
<updated>2020-05-12T10:33:01.000Z</updated>
<content type="html"><![CDATA[<p>Today our self-hosted rocket.chat server suddenly stopped running, and it turns out I am not the only one <a href="https://forums.rocket.chat/t/sudden-502-error/6914">experiencing this problem</a></p>
<p>When I check the server, use the command to check status of rocketchat server and its mongo:</p>
<pre><code class="language-bash">$ service snap.rocketchat-server.rocketchat-server status
# this shows mongodb cannot be connected
$ service snap.rocketchat-server.rocketchat-mongo status
# this shows mongdb is not running
</code></pre>
<p>So apparently there is something wrong with MongoDB. When I check the mongo start log with command:</p>
<pre><code class="language-bash">journalctl -u snap.rocketchat-server.rocketchat-mongo
</code></pre>
<p>There are some errors about the versioning:</p>
<blockquote>
<p>[initandlisten] WiredTiger error (-31802) [1589310297:583337][24694:0x7fbd44f65d00], txn-recover: unsupported WiredTiger file version: this build only supports major/minor versions up to 1/0, and the file is version 2/0: WT_ERROR: non-specific WiredTiger error</p>
</blockquote>
<p>It turns out snap did an update to our rocket.chat server, and updated it to the new version, which now runnign mongodb version 3.6 and changed some database files. Now the database files cannot be run under mongo3.4 anymore (which rocket.server needs).</p>
<p>So I followed this method <a href="https://forums.rocket.chat/t/suddenly-mongod-is-not-starting/4983/2">here</a>:</p>
<ol>
<li>Download version 3.6 in mongodb's website</li>
<li>cd to version 3.6 bin: <code>./mongod --dbpath /var/snap/rocketchat-server/common/</code></li>
<li>in the same 3.6 bin folder, use mongo connect to the server: <code>./mongo</code></li>
<li>now is the key part, run commnad: <code>db.adminCommand( { setFeatureCompatibilityVersion: “3.4” } )</code></li>
<li>exit both above mongo and mongod</li>
<li>cd to <code>/snap/rocketchat-server/current/bin</code></li>
<li>run <code>./mongod --repair --dbpath /var/snap/rocketchat-server/common/</code></li>
<li>once it is done, try start rocketchat-mongo: <code>service snap.rocketchat-server.rocketchat-mongo start</code>, then check its status again: <code>service snap.rocketchat-server.rocketchat-mongo status</code>, it should be running now.</li>
</ol>
<p>The MongoDB is running, but when I use:</p>
<pre><code class="language-bash">service snap.rocketchat-server.rocketchat-server start
</code></pre>
<p>to start the rocket.chat server, it still doesn't work. By checking its log:</p>
<pre><code class="language-bash">journalctl -u snap.rocketchat-server.rocketchat-server
</code></pre>
<p>Found there is a line:</p>
<blockquote>
<p>fs.js:646<br>
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);<br>
^<br>
Error: ENOENT: no such file or directory, open '/snap/rocketchat-server/1436/star.json'</p>
</blockquote>
<p>When I cd to <code>/snap/rocketchat-server/1436/</code> folder, there is no "star.json" file. But the old version 1427 has it: <code>/snap/rocketchat-server/1427/</code></p>
<p>So I guess there is an error in this new version, have to roll it back to previous version and forbid it to update the latest version:</p>
<pre><code class="language-bash">$ snap revert --revision=1427 rocketchat-server
$ snap run --shell rocketchat-server
$ snapctl get snap-refreshing #which returned "true"
$ snapctl set snap-refreshing=false
</code></pre>
<p>Now start the server again:</p>
<pre><code class="language-bash">$ service snap.rocketchat-server.rocketchat-server start
</code></pre>
<p>check the status:</p>
<pre><code class="language-bash">$ service snap.rocketchat-server.rocketchat-server status
</code></pre>
<p>Now it works!!! after spending several hours on this ... what a fun day 😄</p>
<h3 id="reference">Reference:</h3>
<ul>
<li><a href="https://rocket.chat/docs/installation/manual-installation/ubuntu/snaps/">https://rocket.chat/docs/installation/manual-installation/ubuntu/snaps/</a></li>
<li><a href="https://forums.rocket.chat/t/snap-2-1-update-issues/5019">https://forums.rocket.chat/t/snap-2-1-update-issues/5019</a></li>
<li><a href="https://forums.rocket.chat/t/suddenly-mongod-is-not-starting/4983/2">https://forums.rocket.chat/t/suddenly-mongod-is-not-starting/4983/2</a></li>
<li><a href="https://forums.rocket.chat/t/sudden-502-error/6914/10">https://forums.rocket.chat/t/sudden-502-error/6914/10</a></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Copy file content to clipboard in terminal]]></title>
<id>https://bofeng.github.io/post/copy-file-content-to-clipboard-in-terminal/</id>
<link href="https://bofeng.github.io/post/copy-file-content-to-clipboard-in-terminal/">
</link>
<updated>2020-04-19T11:45:32.000Z</updated>
<content type="html"><![CDATA[<p>In Mac you can use <code>pbcopy</code>:</p>
<pre><code class="language-bash">$ pbcopy < hello.txt
</code></pre>
<p>In Linux which is using X11 (I am using ubuntu), you can use <code>xsel</code> command:</p>
<pre><code class="language-bash">$ sudo apt install xsel
$ xsel -b < hello.txt
</code></pre>
<p>To copy some program's output to clipboard, we can use pipe:</p>
<pre><code class="language-bash">$ date | sel -b
</code></pre>
<p>Then if you use "ctrl-v", you will see the copied date.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[How to add "startpage" as default search engine in Firefox?]]></title>
<id>https://bofeng.github.io/post/how-to-add-startpage-as-default-search-engine-in-firefox/</id>
<link href="https://bofeng.github.io/post/how-to-add-startpage-as-default-search-engine-in-firefox/">
</link>
<updated>2020-04-18T13:58:07.000Z</updated>
<content type="html"><![CDATA[<p><a href="startpage.com">StartPage</a> is a search engine will proxy your search to Google but without giving your data to it. In another word, it can give you the same result as Google but protect your privacy.</p>
<p>It is really easy o make it as your default search engine in the address bar in Firefox. If you open the "Perference" in Firefox, and on the "search" part you will see a search engine list similar to this:</p>
<figure data-type="image" tabindex="1"><img src="https://bofeng.github.io/post-images/1587261660967.png" alt="" loading="lazy"></figure>
<p>So there is no "Start Page" there. You can now go to "startpage.com", then on the right of the address bar, click the "dot dot dot" to see more options, from where you can see "Add Search Engine" option, simply click it.</p>
<figure data-type="image" tabindex="2"><img src="https://bofeng.github.io/post-images/1587261742628.png" alt="" loading="lazy"></figure>
<p>Now go back to the "Preference" page, in the "search engine" dropdown list, you can select "Start Page" as your default search engine. Next time, when you type some keyword in the address bar, Firefox will auto do the search in Start Page.</p>
<figure data-type="image" tabindex="3"><img src="https://bofeng.github.io/post-images/1587261981425.png" alt="" loading="lazy"></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Why there is no "gets" function in C standard library?]]></title>
<id>https://bofeng.github.io/post/why-there-is-no-gets-function-in-c-standard-library/</id>
<link href="https://bofeng.github.io/post/why-there-is-no-gets-function-in-c-standard-library/">
</link>
<updated>2020-04-18T08:39:49.000Z</updated>
<content type="html"><![CDATA[<p>If you are new to learn C program, you may find there are many functions like "getc", "putc", "getchar", "putchar", "fgets", "fputs" : every "put" function comes with a corresponding "get" function. But there is a "puts" function while no "gets" function. Why?</p>
<p>Well, the short anwer is, the "gets" function was there before in C89 standard, then it got deprecated in C99 and removed in C11. But let's see why it got removed.</p>
<p>First let's check the function's declaration:</p>
<pre><code class="language-c">char* gets(char* str)
</code></pre>
<p>Basically we pass a pre-allocated "str" buffer to this function, <code>gets</code> will get user's input and save it into this buffer. So we can write some code like this:</p>
<pre><code class="language-c">char buffer[20] = {0};
gets(buffer);
printf("Your input is: %s\n", buffer);
</code></pre>
<p>If we use <code>gcc</code> to compile the code, we can see the error something like this:</p>
<blockquote>
<p>main.c:(.text+0x164): warning: the 'gets' function is dangerous and should not be used.</p>
</blockquote>
<p>We can see using this function is <strong>dangerous</strong>, but if we ignore this warning and go ahead run our program: if we type "hello world" as our input, the program can correctly put this string out.</p>
<p>So why is it dangerous? In our program we allocated a char array which has 20 slots, and our input "hello world" which are 11 characters, will be saved inside this array from position 0. Our "hello world" case works well, but now imagine what if our input has more than 20 characters, then we won't have enough space to save all of these characters:</p>
<pre><code class="language-bash">$ ./main
hello world hello world hello world
Your input is: hello world hello world hello world
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
</code></pre>
<p>In this case, since we don't have enough space to save user's input, the program crashed.</p>
<p>Sometimes this may cause bug that harder to find, suppose we have code like this:</p>
<pre><code class="language-c">#include <stdio.h>
int main() {
char c = 'c';
char arr[10] = {0};
printf("before gets: variable c is %c\n", c);
gets(arr);
printf("after gets: variable c is %c\n", c);
return 0;
}
</code></pre>
<p>Before we compile and run the code, guess what the output would be if in the <code>gets</code> step, we type "hello world".</p>
<p>Your guess might be the program will crash since "hello world" in total are 11 characters (including the white-space in the middel) but our allocated <code>arr</code> only have 10 slots. Well, you are not wrong with the <strong>modern compiler</strong>, by that I mean, if we compile the code and run it:</p>
<pre><code class="language-bash">$ gcc main.c -o main
main.c:(.text+0x51): warning: the 'gets' function is dangerous and should not be used.
$ ./main
before gets: variable c is c
hello world
after gets: variable c is c
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
</code></pre>
<p>But in the old compiler, you might won't have this crashed. The modern <code>gcc</code> will compile with a default "stack-protector" turned on. Let's try to turn it off then run again:</p>
<pre><code class="language-bash">$ gcc main.c -fno-stack-protector -o main
main.c:(.text+0x42): warning: the 'gets' function is dangerous and should not be used.
$ ./main
before gets: variable c is c
hello world
after gets: variable c is d
</code></pre>
<p>Ah, this time the program doesn't crash. But our variable 'c' which is declared before <code>arr</code> has been "magically" changed to the character 'd', which is the last letter in "world".</p>
<p>To understand this, we need to know how the program allocate spaces in stack. Most of the operating system will allocate stack from high address to low address. The stack in our program will be something like this:<br>
<img src="https://bofeng.github.io/post-images/1587244468586.png" alt="" loading="lazy"></p>
<p>When we input "hello world", program will try to save this string from <code>arr[0]</code>, like this:<br>
<img src="https://bofeng.github.io/post-images/1587244621171.png" alt="" loading="lazy"></p>
<p>So technically we still have 11 slots, but the last one is not for our array, it is the space for our variable <code>c</code>. When the program saved the whole string, it put the last letter 'd' in our <code>char c</code> slot - that's why after input our varaible <code>c</code> has been modified to <code>d</code> ! This kind bug is quite dangerous and not easy to find!</p>
<h3 id="the-solution">The solution</h3>
<p>The alternative function we should use is <code>fgets</code>:</p>
<pre><code class="language-c">char* fgets(char *string, int length, FILE * stream);
</code></pre>
<p>So we can change our code to:</p>
<pre><code class="language-c">#include <stdio.h>
int main() {
char c = 'c';
char arr[10] = {0};
printf("before gets: variable c is %c\n", c);
fgets(arr, 10, stdin);
printf("after gets: variable c is %c\n", c);
return 0;
}
</code></pre>
<p>Use <code>gcc</code> to compile and run it:</p>
<pre><code class="language-bash">$ gcc main.c -o main
$ ./main
before gets: variable c is c
hello world
after gets: variable c is c
</code></pre>
<p>Two things changed here: 1) when compile, there is no warning info printed out 2) although our "hello world" contains 11 letters, our program didn't crash anymore and our variable <code>c</code> hadn't been overwritten.</p>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://stackoverflow.com/questions/1345670/stack-smashing-detected">stack smashing detected</a></li>
<li><a href="https://en.wikipedia.org/wiki/C_file_input/output#gets">gets is deprecated in c99</a></li>
<li><a href="https://en.wikibooks.org/wiki/C_Programming/stdio.h/fgets">fgets function</a></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Gnome display settings enable multiple scales]]></title>
<id>https://bofeng.github.io/post/gnome-display-settings-enable-multiple-scales/</id>
<link href="https://bofeng.github.io/post/gnome-display-settings-enable-multiple-scales/">
</link>
<updated>2020-04-16T21:18:15.000Z</updated>
<content type="html"><![CDATA[<p>I was settings up my new System76 laptop (my current Pop!_OS version is based on ubuntu 19.10), the default 1920x1680 resolution is too small for me in the display settings. So I want to make it bigger. There is a "Scale" settings below, and it only has 2 options: 100%, 200%</p>
<p>100% is too small for me, 200% is too big.</p>
<p>To enable other scales, open terminal run command:</p>
<pre><code class="language-bash">$ gsettings set org.gnome.mutter experimental-features "['x11-randr-fractional-scaling']"
</code></pre>
<p>Then back to display settings, you will be able to see other scale values: 125%, 150% and 175%.</p>
<figure data-type="image" tabindex="1"><img src="https://bofeng.github.io/post-images/1587158852197.png" alt="" loading="lazy"></figure>
<h3 id="reference">Reference:</h3>
<ul>
<li><a href="https://www.linuxuprising.com/2019/04/how-to-enable-hidpi-fractional-scaling.html">https://www.linuxuprising.com/2019/04/how-to-enable-hidpi-fractional-scaling.html</a></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Array and Pointer in C]]></title>
<id>https://bofeng.github.io/post/array-and-pointer-in-c/</id>
<link href="https://bofeng.github.io/post/array-and-pointer-in-c/">
</link>
<updated>2020-04-05T03:01:28.000Z</updated>
<content type="html"><![CDATA[<h3 id="array-is-and-is-not-a-pointer">Array is and is not a pointer</h3>
<p>Is array a pointer in C? seems we can treat it as a pointer when we access its element. But sometimes Array has different behavior from pointer.</p>
<p>Take this for example:</p>
<pre><code class="language-c">int main() {
int arr[10] = {0};
printf("size: %ld", sizeof(arr));
return 0;
}
</code></pre>
<p>If <code>arr</code> here is a pointer, then seems this should print out the size of a pointer (4 or 8, depends on the Operating System), but if we compile this code, it prints out 40, so clearly it is <code>sizeof(int) * 10</code>.</p>
<p>I like the quote here:</p>
<blockquote>
<p>The first step to learning C is understanding that pointers and arrays are the same thing. The second step is understanding that pointers and arrays are different.</p>
</blockquote>
<h3 id="pass-array-as-parameter-to-function">Pass array as parameter to function</h3>
<p>Ok, from this <code>sizeof</code> at least we know they are different, but how do we explain this:</p>
<pre><code class="language-c">#include <stdio.h>
void func(int arr[]) {
size_t size = sizeof(arr);
printf("Inside func: %ld\n", size);
}
int main() {
int arr[10] = {0};
size_t size = sizeof(arr);
printf("Inside main: %ld\n", size);
func(arr);
return 0;
}
</code></pre>
<p>We passed <code>arr</code> as parameter to function <code>func</code>, if we compile and run this program, it prints out:</p>
<pre><code>Inside main: 40
Inside func: 8
</code></pre>
<p>Well, although we set the parameter type to be <code>arr[]</code> in function <code>func</code>, but inside the function, <code>arr</code> behaves like a pointer now (it prints 8, instead of 40). So what's the magic here? we call the same <code>sizeof</code> function, why inside main function it prints out 40, and inside <code>func</code>, it prints 8?</p>
<p>This problem bothered me for a while, I decide to check the generate assembly code to see what's the difference for these 2 <code>sizeof</code> call.</p>
<pre><code class="language-bash">$ gcc -S -fverbose-asm main.c
</code></pre>
<p>A file <code>main.s</code> is generated, open this file, first let's check the <code>sizeof</code> inside main function:</p>
<pre><code class="language-assembly"># main.c:10: int arr[10] = {0};
movq $0, -48(%rbp) #, arr
movq $0, -40(%rbp) #, arr
movq $0, -32(%rbp) #, arr
movq $0, -24(%rbp) #, arr
movq $0, -16(%rbp) #, arr
# main.c:11: size_t size = sizeof(arr);
movq $40, -56(%rbp) #, size
</code></pre>
<p>Here the <code>movq</code> is move 8 bytes command. We can see after we defined <code>arr</code>, it first moved 8 bytes for 5 times from the <code>arr</code> address, then for our <code>sizeof</code> call, the compiler immediately set the size to be 40. Ha, so this size would be already set during the compile time, not at run time! The compiler knows <code>arr</code> here is an array's base address, it knows the array's size.</p>
<p>But the <code>sizeof</code> call in <code>func</code> function gave us 8, its assembly code:</p>
<pre><code class="language-assembly"> movq %rdi, -24(%rbp) # arr, arr
# main.c:5: size_t size = sizeof(arr);
movq $8, -8(%rbp) #, size
</code></pre>
<p>We can see here, compiler directly put 8 to the <code>size</code> variable. The reason is when we pass an array to a function, it will be downgraded to a pointer, so although we write the funcion to be <code>func(int arr[])</code>, when we pass the <code>arr</code> to this function, compiler will treat it as <code>func(int* arr)</code>.</p>
<p>But it seems weird to me: the compiler should be definitely smart enough to track the passed parameter and can tell it is an array. The only reasonable explanation I found is the "historical" reason:</p>
<blockquote>
<p>The only purpose of this rule is to maintain backwards compatibility with historical compilers that did not support passing aggregate values as function arguments.</p>
</blockquote>
<p>But anyway, if the C standard says so, ok, at leaset we know when pass array as parameter, it is a pointer. So inside the function, if we want to know how many elements that array contains, we cann't use <code>sizeof(arr)/sizeof(arr[0])</code> any more, because <code>sizeof(arr</code>) will just give us the size of a pointer.</p>
<p>So in the <code>func</code> function, how do we tell the array's size? An obvious way is we can pass another length paramter to tell this <code>func</code> the array length.</p>
<pre><code class="language-c">void func(int arr[], int len) {
// ...
}
</code></pre>
<p>Or we use the array's address as the paramter :</p>
<pre><code class="language-c">void func2(int (*arr)[10]) {
size_t size = sizeof(*arr);
printf("Inside func2: %ld\n", size);
}
</code></pre>
<p>If in our <code>main</code> function, we call this <code>func2</code> as <code>func2(&arr);</code>, it will print <code>Inside func2: 40</code>.</p>
<p>For our <code>func2</code> function, we write a fixed length 10 there. If we want the same trick works for variable length array, we could define the function like this:</p>
<pre><code class="language-c">void func3(int n, int (*arr)[n]) {
size_t size = sizeof(*arr);
printf("Inside func3: %ld\n", size);
}
</code></pre>
<p>Then call it like <code>func3(10, &arr)</code>, it will also print out the size to be 40.</p>
<h3 id="variable-length-array-vla">Variable Length Array (VLA)</h3>
<p>In the above example, we defined a fixed length array, and when compiler sees <code>sizeof</code>, it immidiately know the size. But what if the array's length is determined at the run time? Say if we have code:</p>
<pre><code class="language-c">int main() {}
int len = 0;
scanf("%d", &len);
int arr2[len];
size = sizeof(arr2);
printf("arr2 size: %ld\n", size);
return 0;
}
</code></pre>
<p>Here we take the length from user input, can compiler output the right array size in this case? or it will just treat the array as a pointer now?</p>
<p>If we try it: we type 10 as the length, it prints arr2 size: 40; we type 20 as the length, it prints arr2 size: 80. So although the size determined during run time, the compiler still has a way to tell use the correct size.</p>
<p>If we check the generated assembly code, this time the compiler generated much more than the fixed length:</p>
<pre><code class="language-assembly"># main.c:17: int arr2[len];
movl -124(%rbp), %ecx # len, len.0_19
# main.c:17: int arr2[len];
movslq %ecx, %rax # len.0_19, _1
subq $1, %rax #, _2
movq %rax, -112(%rbp) # _2, D.2335
movslq %ecx, %rax # len.0_19, _4
movq %rax, %r14 # _4, _5
movl $0, %r15d #, _5
movslq %ecx, %rax # len.0_19, _9
movq %rax, %r12 # _9, _10
movl $0, %r13d #, _10
movslq %ecx, %rax # len.0_19, _12
leaq 0(,%rax,4), %rdx #, _24
movl $16, %eax #, tmp122
subq $1, %rax #, tmp103
addq %rdx, %rax # _24, tmp104
movl $16, %edi #, tmp123
movl $0, %edx #, tmp107
divq %rdi # tmp123
imulq $16, %rax, %rax #, tmp106, tmp108
movq %rax, %rdx # tmp108, tmp110
andq $-4096, %rdx #, tmp110
movq %rsp, %rsi #, tmp111
subq %rdx, %rsi # tmp110, tmp111
movq %rsi, %rdx # tmp111, tmp111
.L3:
cmpq %rdx, %rsp # tmp111,
je .L4 #,
subq $4096, %rsp #,
orq $0, 4088(%rsp) #,
jmp .L3 #
.L4:
movq %rax, %rdx # tmp108, tmp112
andl $4095, %edx #, tmp112
subq %rdx, %rsp # tmp112,
movq %rax, %rdx # tmp108, tmp113
andl $4095, %edx #, tmp113
testq %rdx, %rdx # tmp113
je .L5 #,
andl $4095, %eax #, tmp114
subq $8, %rax #, tmp114
addq %rsp, %rax #, tmp115
orq $0, (%rax) #,
.L5:
movq %rsp, %rax #, tmp109
addq $3, %rax #, tmp116
shrq $2, %rax #, tmp117
salq $2, %rax #, tmp118
movq %rax, -104(%rbp) # tmp118, arr2.1
# main.c:18: size = sizeof(arr2);
movslq %ecx, %rax # len.0_19, _14
# main.c:18: size = sizeof(arr2);
salq $2, %rax #, tmp119
movq %rax, -120(%rbp) # tmp119, size
</code></pre>
<p>We won't dive into details for this code (to see the explanation, you can <a href="https://stackoverflow.com/questions/10078283/how-sizeofarray-works-at-runtime">check it here</a>), but we can see for VLA, <code>sizeof</code> can still correctly evaluate the array size in run time..</p>
<h3 id="malloc-and-free">malloc and free</h3>
<p>Another question is, we know if we allocate some memory using <code>malloc</code>, and when we free the memory with the <code>free</code> function, we never pass a second parameter to tell the size (how many memory needs to be free'ed), how does the compiler know that?</p>
<p>The magic is behind the <code>malloc</code> function, for example, when we call the <code>p = malloc(10)</code> function, although it returns us the address for 10 bytes, under the hood, it will allocate more to keep some meta data like the size. For example, a simplest method could be, it allocate 4 bytes + 10 bytes where the first 4 bytes is used to save the size 10, then it returns the memory address starts from the 10 bytes. Later when we call the <code>free(p)</code> function, <code>free</code> can first use p - 4 bytes to read what the size is, then do a proper delete.</p>
<h3 id="takeaways">Takeaways</h3>
<ol>
<li>array and pointer are different</li>
<li>when pass an array as parameter to function, it decays to pointer</li>
<li>we can pass a array pointer as parameter to keep it behaves as an array instead of a pointer</li>
<li>even array's length is determined during the run time, sizeof can still print out correct size</li>
<li>malloc function will allocate more to save the size info such that later the free function can get the correct size to free</li>
</ol>
<h3 id="reference">Reference:</h3>
<ol>
<li><a href="https://www.quora.com/Why-doesn%E2%80%99t-sizeof-a-sizeof-a-0-work-for-an-array-passed-as-a-parameter">https://www.quora.com/Why-doesn%E2%80%99t-sizeof-a-sizeof-a-0-work-for-an-array-passed-as-a-parameter</a></li>
<li><a href="https://stackoverflow.com/questions/851958/where-do-malloc-and-free-store-allocated-sizes-and-addresses">https://stackoverflow.com/questions/851958/where-do-malloc-and-free-store-allocated-sizes-and-addresses</a></li>
<li><a href="https://stackoverflow.com/questions/10078283/how-sizeofarray-works-at-runtime">https://stackoverflow.com/questions/10078283/how-sizeofarray-works-at-runtime</a></li>
</ol>
]]></content>
</entry>
</feed>